bzoj3771 Triple

题意:一共有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去重。

原文地址:https://www.cnblogs.com/Scx117/p/8707219.html