zoukankan      html  css  js  c++  java
  • CF 632E

    本题是一道好题...

    首先我们可以看到,本题其实可以用完全背包跑,但是复杂度不对

    所以我们考虑优化:

    我们知道,如果有三个物品价值分别为a_i,a_j,a_k

    如果允许取一个物品,那么a_i,a_j,a_k都是合法的答案

    如果允许取三个物品,那么a_i+a_j+a_k就是一个合法的价值(废话)

    这是否给了我们一些启示呢?

    如果我们设集合S=egin{Bmatrix} a_1,& a_2 ,&...&a_n end{Bmatrix},构造一个多项式A(x)=sum_{i=0}^{max(a_i)}(iin S?1:0)x^i

    可以发现,如果k=1,那么这个多项式中每个x^i项前系数不为0的i即为合法答案

    如果k=2,那么这个多项式自乘一次,每个x^i项前系数不为0的即为合法答案

    (这一点很好理解,一个x^i项前系数不为0的条件是当且仅当至少存在一对j,k使得在原多项式中x^j,x^k前系数均不为0,且j+k=i,而根据定义,如果x^j,x^k前系数不为0,说明jin S,kin S,那么也就是选出了两件物品嘛)

    因此,对于任意的k,我们只需要将构造出的多项式自乘k次,然后找出系数不为0的项,输出答案即可

    接下来谈几个细节:

    首先,本题数据范围过大,因此FFT是难以通过的,建议使用NTT

    其次,NTT的常用模数会被卡(比如998244353和1004535809),因此需要用一些不常见的东西,比如469762049(原根为3),这个东西亲测不会卡(如果你非要用前两个,请使用双模)

    然后就结束了

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define ll long long
    using namespace std;
    const double pi=acos(-1.0);
    //const ll mode1=998244353;
    const ll mode=469762049;
    int to[(1<<21)+5];
    int n,lim=1,l;
    int m,k;
    ll pow_mul(ll x,ll y)
    {
        ll ans=1;
        while(y)
        {
            if(y&1)ans=ans*x%mode;
            x=x*x%mode,y>>=1;
        }
        return ans;
    }
    void NTT(ll *a,int len,int k)
    {
        for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);
        for(int i=1;i<len;i<<=1)
        {
            ll w0=pow_mul(3,(mode-1)/(i<<1));
            for(int j=0;j<len;j+=(i<<1))
            {
                ll w=1;
                for(int o=0;o<i;o++,w=w*w0%mode)
                {
                    ll w1=a[j+o],w2=a[j+o+i]*w%mode;
                    a[j+o]=(w1+w2)%mode,a[j+o+i]=((w1-w2)%mode+mode)%mode;
                }
            }
        }
        if(k==-1)for(int i=1;i<(len>>1);i++)swap(a[i],a[len-i]);
    }
    ll a[(1<<21)+5],b[(1<<21)+5],c[(1<<21)+5];
    ll temp[(1<<21)+5];
    int n1,n2;
    void pow_mul()
    {
        b[0]=1;
        while(k)
        {
            if(k&1)
            {
                lim=1,l=0;
                while(lim<=2*max(n1,n2))lim<<=1,l++;
                for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));
                for(int i=0;i<lim;i++)temp[i]=a[i];
                NTT(b,lim,1),NTT(temp,lim,1);
                for(int i=0;i<lim;i++)c[i]=temp[i]*b[i]%mode;
                NTT(c,lim,-1);
                ll inv=pow_mul(lim,mode-2);
                for(int i=0;i<lim;i++)b[i]=c[i]*inv%mode,c[i]=temp[i]=0;
                n2+=n1;
            }
            lim=1,l=0;
            while(lim<=2*n1)lim<<=1,l++;
            for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));
            NTT(a,lim,1);
            for(int i=0;i<lim;i++)c[i]=a[i]*a[i]%mode;
            NTT(c,lim,-1);
            ll inv=pow_mul(lim,mode-2);
            for(int i=0;i<lim;i++)a[i]=c[i]*inv%mode,c[i]=0;
            k>>=1;
            n1<<=1;
        }
        for(int i=0;i<n2;i++)if(b[i]>0)printf("%d ",i);
        printf("
    ");
    }
    int main()
    {
        scanf("%d%d",&m,&k);
        n1=1000,n2=1000;
        for(int i=1;i<=m;i++)
        {
            int t;
            scanf("%d",&t);
            a[t]=1;
        }
        pow_mul();
        return 0;
    }
  • 相关阅读:
    软解析和硬解析
    oracle存储过程常用技巧
    jquery-1.11.1.js
    JavaScript遍历table
    JavaScript向select下拉框中添加和删除元素
    glog
    DDL引发的对象invalidation
    模拟cursor pin S wait on X
    rsync 排除目录
    JavaScript解决select下拉框中的内容太长显示不全的问题
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10805663.html
Copyright © 2011-2022 走看看