BZOJ 3771 Triple (FFT+生成函数+容斥)

 

题面传送门

题目大意:给你互不相同的$n$个数,在其中任选$1$~$3$个数,不能重复选数,设它们的和为$S$。对于所有可能的$S$,求选出的数和为$S$方案总数,选数没有顺序。

先对所有的数弄一个生成函数$A$,有数的位置权值为$1$

如果我们要选$x$个数,求方案数。只需要对$A$求$x$次卷积。

然而题目要求选数是无序的,且不能重复选数

选$1$个数的方案就是$A$

选$2$个数时,需要去掉重复选同一个数的情况,即$(A*A-B)/2$,$B$是相同的数选$2$次时的生成函数

选$3$个数的情况更复杂

1.首先要去掉重复选$3$次同一个数的情况,设相同的数选$3$次时的生成函数为$C$

2.还要去掉 选$2$次相同的数,再选另一个数的情况,这种情况的生成函数是$3*B*A$

然而情况二还可能把情况一去掉,需要再加回来

最终答案就是$A+(A*A-B)/2+(A*A*A-3*B*A+2*C)/6$

 

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #define N1 (1<<18)+10
 6 #define ll long long 
 7 #define dd double
 8 using namespace std;
 9  
10  
11 int gint()
12 {
13     int ret=0,fh=1; char c=getchar();
14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
16     return ret*fh;
17 }
18  
19 int r[N1];
20 const dd pi=acos(-1);
21 struct cp{
22 dd x,y;
23 friend cp operator + (const cp &s1,const cp &s2){ return (cp){s1.x+s2.x,s1.y+s2.y}; }
24 friend cp operator - (const cp &s1,const cp &s2){ return (cp){s1.x-s2.x,s1.y-s2.y}; }
25 friend cp operator * (const cp &s1,const cp &s2){ return (cp){s1.x*s2.x-s1.y*s2.y,s1.y*s2.x+s1.x*s2.y}; }
26 }a[N1],b[N1],c[N1],f[N1];
27  
28 void init()
29 {
30     memset(a,0,sizeof(a));
31     memset(b,0,sizeof(b));
32     memset(c,0,sizeof(c));
33 }
34  
35 void FFT(cp *s,int len,int type)
36 {
37     int i,j,k; cp wn,w,t;
38     for(i=0;i<len;i++) if(i<r[i]) swap(s[i],s[r[i]]);
39     for(k=2;k<=len;k<<=1)
40     {
41         wn=(cp){cos(2.0*pi*type/k),sin(2.0*pi*type/k)};
42         for(i=0;i<len;i+=k)
43         {
44             w=(cp){1,0};
45             for(j=0;j<(k>>1);j++,w=w*wn)
46             {
47                 t=w*s[i+j+(k>>1)];
48                 s[i+j+(k>>1)]=s[i+j]-t;
49                 s[i+j]=s[i+j]+t;
50             }
51         }
52     }
53 }
54 int n,ma,len,L;
55 int v[N1],s1[N1],s2[N1],s3[N1],ans[N1];
56  
57 int main()
58 {
59     scanf("%d",&n); int i;
60     for(i=1;i<=n;i++) v[i]=gint(),ma=max(ma,v[i]),s1[v[i]]++; // 1 -> A
61      
62     for(len=1,L=0;len<3*ma;len<<=1,L++);
63     for(i=1;i<=n;i++) a[v[i]].x=1,b[v[i]<<1].x=1;//c[v[i]*3].x=1;
64     for(i=0;i<len;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
65     FFT(a,len,1); FFT(b,len,1);// FFT(c,len,1);
66      
67     for(i=0;i<len;i++) f[i]=a[i]*a[i]; // 2 -> (A*A-B)/2
68     FFT(f,len,-1);
69     for(i=1;i<=n;i++) s2[v[i]<<1]--;
70     for(i=0;i<len;i++) s2[i]=(int)(f[i].x/len+0.1);
71     for(i=0;i<len;i++) s2[i]/=2;
72      
73     for(i=0;i<len;i++) f[i]=a[i]*a[i]*a[i]; // 3 -> (A*A*A-3*B*A+2*C)/6
74     FFT(f,len,-1);
75     for(i=0;i<len;i++) s3[i]+=(int)(f[i].x/len+0.1);
76     for(i=0;i<len;i++) f[i]=a[i]*b[i];
77     FFT(f,len,-1);
78     for(i=0;i<len;i++) s3[i]-=( (int)(f[i].x/len+0.1) )*3;
79     for(i=1;i<=n;i++) s3[v[i]*3]+=2;
80     for(i=0;i<len;i++) s3[i]/=6;
81      
82     for(i=1;i<=len;i++) if(s1[i]+s2[i]+s3[i]>0) 
83         printf("%d %d\n",i,s1[i]+s2[i]+s3[i]);
84      
85     return 0;
86 }

 

posted @ 2019-02-01 11:00  guapisolo  阅读(160)  评论(0编辑  收藏  举报