CF 632E
本题是一道好题...
首先我们可以看到,本题其实可以用完全背包跑,但是复杂度不对
所以我们考虑优化:
我们知道,如果有三个物品价值分别为,
如果允许取一个物品,那么都是合法的答案
如果允许取三个物品,那么就是一个合法的价值(废话)
这是否给了我们一些启示呢?
如果我们设集合,构造一个多项式
可以发现,如果k=1,那么这个多项式中每个项前系数不为0的i即为合法答案
如果k=2,那么这个多项式自乘一次,每个项前系数不为0的即为合法答案
(这一点很好理解,一个项前系数不为0的条件是当且仅当至少存在一对j,k使得在原多项式中前系数均不为0,且,而根据定义,如果前系数不为0,说明,那么也就是选出了两件物品嘛)
因此,对于任意的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("\n"); } 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; }