• CF1225G To Make 1


    Link
    (a_i)总共除过(b_i)次,那么我们要做的就是找到一组(b)使得(sumlimits_{i=0}^na_ik^{-b_i}=1)
    显然存在合法的(b)就存在一组合法的合并方案,构造方法如下:

    每次选择两个(b_i)相同的最大的合并,然后递归进子问题。
    因为(a_i mid k),所以每次不可能只出现一个最大值,这样是肯定无解的。

    考虑状压dp,设(f_{s,x})表示存在一组(b)使得(sumlimits_{iin s}a_ik^{-b_i}=x)
    转移非常简单就不讲了,注意到复杂度是(O(2^nnk))会爆炸,用bitset优化即可做到(O(frac{2^nnk}{omega}))

    #include<queue>
    #include<cstdio>
    #include<bitset>
    #include<utility>
    #define se second
    const int N=2007,M=65537;
    using std::pair;
    using pi=pair<int,int>;
    int a[16],c[16],low[M],n,k,U,s;
    std::bitset<N>f[M];
    std::priority_queue<pi>q;
    int read(){int x;scanf("%d",&x);return x;}
    void dfs(int st,int x)
    {
        if(!st) return;
        for(;x*k<=s&&f[st][x*k];x*=k) for(int i=0;i<n;++i) if(st>>i&1) ++c[i];
        for(int i=0;i<n;++i) if(st>>i&1&&x>=a[i]&&f[st^1<<i][x-a[i]]) return dfs(st^1<<i,x-a[i]);
    }
    int main()
    {
        n=read(),k=read(),U=(1<<n)-1,f[0][0]=1;
        for(int i=0;i<n;++i) s+=(a[i]=read()),low[1<<i]=i;
        for(int i=1;i<=U;++i) low[i]=low[i&-i];
        for(int i=1;i<=U;++i)
        {
    	for(int j=0;j<n;++j) if(i>>j&1) f[i]|=f[i^1<<j]<<a[j];
    	for(int j=s/k;j;--j) if(f[i][j*k]) f[i][j]=1;
        }
        if(!f[U][1]) return puts("NO"),0;
        puts("YES"),dfs(U,1);
        for(int i=0;i<n;++i) q.push({c[i],a[i]});
        for(pi u,v;q.size()>1;)
        {
    	u=q.top(),q.pop(),v=q.top(),q.pop(),printf("%d %d
    ",u.se,v.se),u.se+=v.se;
    	while(!(u.se%k)) u.se/=k,--u.first;
    	q.push(u);
        }
    
    }
    
  • 相关阅读:
    Java之字符串String,StringBuffer,StringBuilder
    去除前后空格,Oracle和SQLSERVER都适用。ltrim(rtrim(’ ‘))
    java IO,bufferedReader类
    Python与操作系统有关的模块
    u盘引导制作工具
    Shell中判断语句if中-z至-d的意思
    shell脚本输出带颜色字体
    MySQL数据库权限管理
    vi/vim使用进阶: 文件浏览和缓冲区浏览
    (转载)跟我一起学习VIM
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12246968.html
Copyright © 2020-2023  润新知