一个生成函数,用FFT做乘法
重复的部分我们考虑用容斥原理来解决。
为了方便描述我们不妨设三个多项式。
第一个是仅取一个而构成的多项式。->x
第二个是仅取相同的两个而构成的多项式。->y
第三个是仅取相同的三个而构成的多项式。->z
对于本题有三种情况。
第一种是取一个,显然直接将x加到答案就好。
第二种是取两个,则需要一小步容斥,即(x*x-y)/2
第三种是取三个,则需要进一步容斥,即(x*x*x-3*x*y+2*z)/6
#include<cstdio> #include<cstdlib> #include<cmath> #include<complex> #include<algorithm> #define PI acos(-1.0) #include<cstring> #define iD ((int)(x1[i].real()+0.1)) #define iE ((int)(x2[i].real()+0.1)) #define iF ((int)(x3[i].real()+0.1)) #define cpy(x,y) memcpy(x,y,sizeof(y)) using namespace std; typedef complex<double> E; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(int &x) { char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } int n,m; E x[150005],x1[150005],x2[150005],x3[150005]; int ans[150005]; int L,R[150005]; inline void FFT(E *a,int r){ for (int i=0;i<n;i++) if (i<R[i]) swap(a[i],a[R[i]]); for (int i=1;i<n;i<<=1) { E wn(cos(PI/i),r*sin(PI/i)); for (int j=0;j<n;j+=(i<<1)) { E w(1,0); for (int k=0;k<i;k++,w*=wn) { E x=a[j+k],y=w*a[j+k+i]; a[j+k]=x+y; a[j+k+i]=x-y; } } } if (r==-1) for (int i=0;i<n;i++) a[i]/=n; } E a[150005],b[150005]; inline void MUl(E *A,E *B) { for (int i=0;i<n;i++) a[i]=A[i]; for (int i=0;i<n;i++) b[i]=B[i]; FFT(a,1); FFT(b,1); for (int i=0;i<n;i++) a[i]*=b[i]; FFT(a,-1); for (int i=0;i<n;i++) A[i]=a[i]; // for (int i=0;i<25;i++) printf("%d ",(int)(a[i].real()+0.1)); printf("\n"); } int main() { int in,iv; freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(in); for (int i=1;i<=in;i++) read(iv),x[iv]+=1; n=40000; m=3*n; for (n=1;n<=m;n<<=1) L++; for(int i=0;i<n;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); for (int i=0;i<n;i++) { x1[i]=x[i]; if (i*2<n) x2[i*2]=x1[i]; if (i*3<n) x3[i*3]=x1[i]; } for (int i=1;i<=m;i++) ans[i]+=(int)(x1[i].real()+0.1); MUl(x1,x); for (int i=1;i<=m;i++) ans[i]+=(iD-iE)/2; MUl(x2,x); MUl(x1,x); for (int i=1;i<=m;i++) ans[i]+=(iD-3*iE+2*iF)/6; for (int i=1;i<=m;i++) if (ans[i]) printf("%d %d\n",i,ans[i]); return 0; }