题意:给你n种物品,每种无限个,问恰好取k个物品能组成哪些重量.n<=1000,k<=1000,每种物品的重量<=1000.
我们搞出选取一种物品时的生成函数,那么只要对这个生成函数求k次幂就可以了.结果会很大所以我们可以在模意义下NTT来搞.然而会有一个问题,就是算出来的系数可能恰好是模数的倍数,比如说我们只用998244353就会WrongAnswer on test 20.然后我试了和异化多肽的模数1005060097一块来,然后,WrongAnswer on test 5…Excuse me? 然后看数据,k=1000,物品重量也比较大,分解一下1005060096,发现是2^19*27*71,然后我意识到了:这道题最大需要做最高次数2^20的NTT…然后枚举出了几个2^22*k+1的质数做模数…过了…
以下是枚举出来的一些模数和原根,均为2^22*k+1的形式(不保证k为奇数)
104857601 3
113246209 7
138412033 5
155189249 6
163577857 23
167772161 3
230686721 6
377487361 7
415236097 5
469762049 3
595591169 3
645922817 3
666894337 5
683671553 3
754974721 11
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int mod,rt; int qpow(int a,int x){int ans=1;for(;x;x>>=1,a=a*1ll*a%mod)if(x&1)ans=ans*1ll*a%mod;return ans;} void ntt(int *a,int n,int sign){ for(int i=1,j=0,k;i<n;++i){ k=n;do j^=(k>>=1);while(j<k);if(i<j)swap(a[i],a[j]); } for(int j=2;j<=n;j<<=1){ int m=j>>1;long long wn=qpow(rt,(mod-1+sign*(mod-1)/j));; for(int *p=a;p!=a+n;p+=j){ long long w=1; for(int k=0;k<m;++k,w=w*wn%mod){ long long t=w*p[m+k]%mod;p[m+k]=(p[k]-t+mod)%mod;p[k]=(p[k]+t)%mod; } } } if(sign==-1){ long long inv=qpow(n,mod-2);for(int i=0;i<n;++i)a[i]=a[i]*inv%mod; } } const int maxn=1048576*4; int a[maxn],w[maxn]; int ans[maxn]; int N; int n,k; void NTT(int MOD,int RT){ mod=MOD;rt=RT;memset(a,0,sizeof(a)); for(int i=1;i<=n;++i)a[w[i]]=1; ntt(a,N,1); for(int i=0;i<N;++i)a[i]=qpow(a[i],k); ntt(a,N,-1);//printf("%d ",ans[1]); for(int i=0;i<N;++i)if(a[i])ans[i]=1; } int main(){ scanf("%d%d",&n,&k); int Max=0; for(int i=1;i<=n;++i){scanf("%d",&w[i]);if(w[i]>Max)Max=w[i];} N=1; while(N<=Max*k)N<<=1; //printf("%d ",ans[0]); NTT(998244353,3);NTT(163577857,23);NTT(415236097,5); for(int i=0;i<N;++i)if(ans[i])printf("%d ",i); return 0; }