zoukankan      html  css  js  c++  java
  • 【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
    ");
    }
    
    
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法提高 P0404
    Java实现 蓝桥杯VIP 算法训练 排列问题
    Java实现 蓝桥杯VIP 算法训练 排列问题
    Java实现 蓝桥杯VIP 算法训练 排列问题
    Java实现 蓝桥杯VIP 算法训练 排列问题
    关于模态/非模态对话框不响应菜单的UPDATE_COMMAND_UI消息(对对WM_INITMENUPOPUP消息的处理)
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090929.html
Copyright © 2011-2022 走看看