题意:一共有n个物品,价值都不同。问取一件或两件或三件物品,可能得到的总价值有哪些,并对于每个价值输出有多少种和为该价值的取物方案(无序)。
n,ai<=40000;
标程:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<complex> 5 #include<cmath> 6 using namespace std; 7 typedef complex<double> p; 8 const double pi=acos(-1); 9 const int N=160000; 10 int n,l,pos[N],f[N],g[N],tt[N],a[N],Max,inv_n,ans[N]; 11 p x[N],y[N],xx[N],tmp[N]; 12 void init() 13 { 14 l=0; 15 for (;(1<<l+1)<=n;l++); 16 n=1<<++l; 17 for (int i=1;i<=n;i++)//循环从1开始 18 pos[i]=(i&1)?pos[i-1]|(1<<l-1):pos[i>>1]>>1; 19 } 20 void fft(p *a) 21 { 22 for (int i=0;i<=n;i++) tmp[i]=a[pos[i]];//循环从0开始 23 int len=1; 24 for (int i=0;i<l;i++) 25 { 26 p wn=p(cos(-pi/len),sin(-pi/len)); 27 for (int j=0;j<n;j+=len*2) 28 { 29 p w=p(1,0); 30 for (int k=j;k<j+len;k++) 31 { 32 p l=tmp[k],r=tmp[k+len]*w; 33 tmp[k]=l+r;tmp[k+len]=l-r; 34 w*=wn; 35 } 36 } 37 len<<=1; 38 } 39 for (int i=0;i<=n;i++) a[i]=tmp[i];//复数不要用memset 40 } 41 void fft(p *a,int op) 42 { 43 fft(a); 44 if (op==-1) 45 { 46 reverse(a+1,a+n);//注意第0位和第n位不交换 47 for (int i=0;i<=n;i++) a[i]/=n; 48 } 49 } 50 int main() 51 { 52 scanf("%d",&n);int nn=n; 53 for (int i=1;i<=n;i++) scanf("%d",&a[i]),xx[a[i]]=(1),y[2*a[i]]=(1),Max=max(Max,a[i]); 54 n=Max*3;init(); 55 //选三个东西 56 fft(xx,1);fft(y,1); 57 for (int i=0;i<=n;i++) y[i]=y[i]*xx[i]; 58 for (int i=0;i<=n;i++) x[i]=xx[i]*xx[i]*xx[i]; 59 fft(x,-1);fft(y,-1); 60 for (int i=0;i<=n;i++) g[i]=(int)(x[i].real()+0.5),f[i]=(int)(y[i].real()+0.5); 61 for (int i=0;i<=n;i++) g[i]-=f[i]*3; 62 for (int i=1;i<=nn;i++) g[3*a[i]]+=2; 63 for (int i=0;i<=n;i++) ans[i]=g[i]/6; 64 //选两个东西 65 for (int i=0;i<=n;i++) xx[i]=xx[i]*xx[i]; 66 fft(xx,-1); for (int i=0;i<=n;i++) g[i]=(int)(xx[i].real()+0.5); 67 for (int i=1;i<=nn;i++) g[2*a[i]]--; 68 for (int i=0;i<=n;i++) ans[i]+=g[i]/2; 69 //选一个东西 70 for (int i=1;i<=nn;i++) ans[a[i]]++; 71 for (int i=0;i<n;i++) if (ans[i]) printf("%d %d ",i,ans[i]);//注意最后一个n不纳入计算 72 return 0; 73 }
易错点:1.fft太久不写,有点生疏了。
2.注意循环的开始从1还是0.
3.reverse(a+1,a+n).
4.最后n位置不计算。
5.不要以为没有模数就可以乱设模数,要用复数。
题解:fft+生成函数
比如选一个价值为2的物品,设为x^2.
最后多项式f(x)中x^k的系数表示总价值为k的方案数。
所以多项式卷积两次就是选两个,卷三次就是选三个物品。
还要减去重复计算的,以及去掉本质相同的东西。容斥。
以取三个物品为例:f(x)^3任意选三个,减去f2(x)*f(x)选两个一样第三个任选*3(三种排列方式),加上同一个数选三次的方案*2(被多减了两次)。
剩下的就是有序的不重复取同一个东西的取法数。除以6去重。