• 【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
    ");
    }
    
    
  • 相关阅读:
    最大流
    CF Round #634
    CF Round #633
    Sunday
    Pikachu——RCE(远程系统命令、代码执行)
    Pikachu——SQL Inject
    Pikachu——CSRF(跨站请求伪造)
    Pikachu——XSS(反射型,存储型,DOM型,盲打,过滤,htmlspecialchars(),href输出,js输出)
    Pikachu暴力破解——token防爆破?
    Pikachu暴力破解——验证码绕过(on client)
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090929.html
Copyright © 2020-2023  润新知