【Codeforces#596】E. To Make 1

Descripton

传送门

  • 有n个数不被k整除的数a[i]。
  • 每一次选择两个数x,y,删去这两个数并加入f(x+y):
    f(x)=(xmod  k=0)?f(x/k):xf(x)=(x mod k=0)?f(x/k):x
  • 求最后能不能得到1。如果能,输出每一次将哪两个数删去。
  • n<=16,a[i]sum a[i],k<=2000

Solution

  • 一道让我无从下手的题目。
  • 由于会除以k,所以最终的数可以表示成aikbi=1sum a_i*k^{-b_i}=1
  • 如果我们知道了b的话,就可以推出操作的方案了。
  • 具体来说,假设B=max(bi)B=max(b_i),一定有两个bi同时等于B。否则上式两边同时乘kB就变成
    a+aikBbi=kBa+sum a_i*k^{B-b_i}=k^B
  • a不是k的倍数,左边不是,右边是,不成立。
  • 那么这样就可以将这两个数和在一起了。并加入新的a和b。这个等式还是成立的。直到最后一个。
  • 现在需要求出b。
  • 状压DP,设f[S][x]=0/1,表示已经选了S集合里面的ai,aikbi=xsum a_i*k^{-b_i}=x是否成立。
  • 每一次枚举一个没有选过的a,转移到f[S+(1<<i)][x+ai]。
  • 并且可以转移到f[S][x/k].
  • 这个转移相当于是按照bi从小到大的顺序加入a。
  • 最后判定f[(1<<n)-1][1]。倒退回去即可得到b。
  • 但是这样转移是O(2nna[i])O(2^nnsum a[i])的。考虑把x那一维压进bitset里面。那前一个转移到x+ai的转移就可以通过左移ai位再或过去完成(这个我想了好久,之前一直以为是将S压起来)。时间除以32就可以过了。
  • 总之,这题需要先化简每一个a对答案的贡献,再DP计算出b,用bitset优化。再通过b还原,其中还需要发现合并的性质。
  • 一道神奇的题目。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<bitset>
#define maxn 16
#define maxm 2005
using namespace std;

int n,k,a[maxn],sum,i,j,S,x,cnt,b[maxn];
bitset<maxm> f[1<<maxn];

void dg(int tot){
	if (tot==n) return; 
	int B=0;
	for(int i=0;i<=n-tot;i++) 
		B=max(B,b[i]);
	int t1=0,t2=0;
	for(int i=0;i<=n-tot;i++) if (b[i]==B){
		if (!t1) t1=i; else 	
			{t2=i;break;}
	}
	printf("%d %d
",a[t1],a[t2]);
	int cnt=0,tmp;
	for(tmp=a[t1]+a[t2];tmp%k==0;tmp/=k) cnt++;
	b[t1]=B-cnt,a[t1]=tmp;
	for(int i=t2;i<n-tot;i++) a[i]=a[i+1],b[i]=b[i+1];
	dg(tot+1);
}

int main(){
	scanf("%d%d",&n,&k);
	for(i=0;i<n;i++) scanf("%d",&a[i]),sum+=a[i];
	f[0][0]=1;
	for(S=0;S<1<<n;S++) if (f[S].count()>0){
		for(i=sum;i>=1;i--) if (i%k==0)
			f[S][i/k]=f[S][i/k]|f[S][i];
		for(i=0;i<n;i++) if (!((S>>i)&1))
			f[S|(1<<i)]=f[S|(1<<i)]|(f[S]<<a[i]);
	}
	if (f[(1<<n)-1][1]){
		printf("YES
");
		S=(1<<n)-1,x=1,cnt=0;
		for(i=1;i<=n;i++){
			while (x*k<=sum&&f[S][x*k]) x*=k,cnt++;
			for(j=0;j<n;j++) if (x>=a[j]&&(S&(1<<j))&&(f[S^(1<<j)][x-a[j]])){
				b[j]=cnt,S^=1<<j,x-=a[j];
				break;
			}
		}
		dg(1);
	} else printf("NO
");
}

原文地址:https://www.cnblogs.com/DeepThinking/p/13090929.html