题意:
问n个物品选出K个可以拼成的体积有哪些。
解法:
多项式裸题,注意到本题中 $A(x)^K$ 的系数会非常大,采用NTT优于FFT。
NTT 采用两个 $2^t+1$ 质数,求原根 $g_n$ 后用 $g_n^1 $~$ g_n^{P-1}$ 的循环代替复数向量的旋转。
注意逆的 $w_n$ 是 $g_n ^ { - frac{P-1}{len} }$,并且要用两个质数保证正确即可,$O(nlogn)$。
#include <bits/stdc++.h> #define PI acos(-1) #define P1 998244353LL #define P2 469762049LL #define LL long long #define gn 3 const int N = 500010; using namespace std; int R[N<<2]; LL qpow(LL x,int n,LL P) { LL ans = 1; for(;n;n>>=1,x = x*x % P) if(n&1) ans = ans*x % P; return ans; } void DFT(LL a[],int n,int tp_k,LL P) { for(int i=0;i<n;i++) if(i<R[i]) swap(a[i],a[R[i]]); for(int d=1;d<n;d<<=1) { LL wn = qpow(gn, (P-1)/(d<<1),P); if(tp_k == -1) wn = qpow(wn, P-2,P); for(int i=0;i<n;i += (d<<1)) { LL wt = 1; for(int k=0;k<d;k++, wt = wt*wn % P) { LL A0 = a[i+k], A1 = wt * a[i+k+d] % P; a[i+k] = A0+A1; a[i+k+d] = A0+P-A1; if(a[i+k] >= P) a[i+k] -= P; if(a[i+k+d] >= P) a[i+k+d] -= P; } } } LL inv = qpow(n, P-2,P); if(tp_k==-1) for(int i=0;i<n;i++) a[i] = a[i] * inv % P; } LL A[N<<2],B[N<<2]; int main() { //freopen("test.txt","w",stdout); int n,K; cin>>n>>K; int L = 0,tot; while((1<<L)<1000*K) L++; tot = (1<<L); for(int i=1;i<tot;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); for(int i=1,x;i<=n;i++) scanf("%d",&x), A[x] = 1, B[x] = 1; DFT(A,tot,1,P1); for(int i=0;i<tot;i++) A[i] = qpow(A[i], K, P1); DFT(A,tot,-1,P1); DFT(B,tot,1,P2); for(int i=0;i<tot;i++) B[i] = qpow(B[i], K, P2); DFT(B,tot,-1,P2); for(int i=0;i<tot;i++) if(A[i] || B[i]) printf("%d ",i); printf(" "); return 0; }