題意:連結
方法: FFT+容斥原理
解析:
這東西其實就是指數型母函數。
所以剛開始讀入的值我們都把它前面的係數置為1。
然後其實就是個多項式乘法了。
最大範圍顯然是讀入的值中的最大值乘三,對於本題的話是12W。
用FFT最佳化的話,達到了O(nlogn),顯然可過。
但是這裡有一個問題,就是如何處理重複的部分。
重複的部分我們考慮用容斥原理來解決。
為了方便描述我們不妨設三個多項式。
第一個是僅取一個而構成的多項式。->x
第二個是僅取相同的兩個而構成的多項式。->y
第三個是僅取相同的三個而構成的多項式。->z
對於本題有三種情況。
第一種是取一個,顯然直接將x加到答案就好。
第二種是取兩個,則需要一小步容斥,即(x*x-y)/2
第三種是取三個,則需要進一步容斥,即(x*x*x-3*x*y+2*z)/6
至於第三種,簡單說明一下,將x*x*x算上是代表了所有取三個的情況,減去3*x*y其實是減掉一個x*y/2,即選兩個相同的再除以排列數,因為大情況是3!重複,故添了個係數,減兩個相同的肯定包含三個相同的,所以要加回來兩個。
最後統計答案即可。
代碼:
#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define N 131072#define pi acos(-1)using namespace std;int n; struct complex{ double r,i; complex(double x=0.0,double y=0.0){r=x,i=y;} complex operator + (const complex a) {return complex(a.r+r,a.i+i);} complex operator - (const complex a) {return complex(r-a.r,i-a.i);} complex operator * (const complex a) {return complex(r*a.r-i*a.i,r*a.i+i*a.r);}}a[N+10],b[N+10],c[N+10],d[N+10];int rev[N+10];void FFT(complex *a,int f){ for(int i=0;i<n;i++)if(i<rev[i])swap(a[i],a[rev[i]]); for(int h=2;h<=n;h<<=1) { complex wn(cos(2*pi*f/h),sin(2*pi*f/h)); for(int i=0;i<n;i+=h) { complex w(1,0); for(int j=0;j<(h>>1);j++,w=w*wn) { complex t=a[i+j+(h>>1)]*w; a[i+j+(h>>1)]=a[i+j]-t; a[i+j]=a[i+j]+t; } } } if(f==-1)for(int i=0;i<n;i++)a[i].r/=n;}int main(){ int ma=-1; scanf("%d",&n);n--; for(int i=0;i<=n;i++) { int x; scanf("%d",&x); a[x].r=1,b[2*x].r=1,c[3*x].r=1; ma=max(ma,3*x); } int m=ma,L=0; for(n=1;n<=m;n<<=1)L++; for(int i=0;i<n;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)); FFT(a,1),FFT(b,1),FFT(c,1); for(int i=0;i<=n;i++) { complex tmp(1.0/6.0,0); complex tmp2(3.0,0); complex tmp3(2.0,0); complex tmp4(1.0/2.0,0); d[i]=d[i]+(a[i]*a[i]*a[i]-tmp2*a[i]*b[i]+tmp3*c[i])*tmp; d[i]=d[i]+(a[i]*a[i]-b[i])*tmp4; d[i]=d[i]+a[i]; } FFT(d,-1); for(int i=0;i<=n;i++) { int print=(int)(d[i].r+0.1); if(print!=0)printf("%d %d\n",i,print); } }