题目大意:给定n把斧头的价值,问在这些斧头中随便挑一个两个或三个,价值总和分别有多少种
拿这道题练了练FFT的理解程度
首先我们考虑一个函数F(x)=a0+a1x^1+a2x^2+........无穷无尽(当然到后面系数可能是0)
我们称这个为生成函数
接着我们可以把每一项的系数想象成这种斧头的数量,比如当你有一把1斧头,一把3斧头,生成函数就是
F(x)=x+x^3
这个东西有什么好处呢?我们发现当取一把斧头的时候,每项系数就是这种方案对应的答案
而如果我们把这个函数平方一下,就是取两把斧头(不考虑重复)时的答案
立方一下就得到了三把斧头的答案
这一步用FFT只需要O(NlogN)的复杂度!
然后就是去重,我们发现,将原来多项式的每一项系数挪到平方项的位置就正好是去两个斧头重复的方案,例如上面那个例子
F(x)平方之后得到了F(x)^2=x^6+2*x^4+x^2
将F(x)挪动系数之后就变成了G(x)=x^2+x^6
二者相减再除以二就是选取两把斧头不重复的方案数
稍稍复杂一些,我们也可以推出三把斧头不重复的方案数,具体看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 131072*4+10
using namespace std;
const double pi=acos(-1);
struct E{double s,x;};
E operator-(const E&x,const E&y){return (E){x.s-y.s,x.x-y.x};}
E operator+(const E&x,const E&y){return (E){x.s+y.s,x.x+y.x};}
E operator*(const E&x,const E&y){return (E){x.s*y.s-x.x*y.x,x.s*y.x+x.x*y.s};}
E omg[N];
void FFT(E *A,E *B,int s,int le,int t)
{
if(le==1){B[s]=A[s];return;}
int l=le/2,i;
for(i=0;i<l;i++)
B[s+i]=A[s+2*i],B[s+l+i]=A[s+2*i+1];
FFT(B,A,s,l,t*2);
FFT(B,A,s+l,l,t*2);
for(i=0;i<l;i++)
{
B[s+i]=A[s+i]+omg[i*t]*A[s+i+l];
B[s+l+i]=A[s+i]-omg[i*t]*A[s+i+l];
}
}
E ffta[N],fftb[N],fftc[N];
void AFFT(int *A,int *B,int *C,int n)
{
int i=1;
while(i<n) i*=2;
n=2*i;
for(i=0;i<=n;i++)
{
ffta[i]=(E){A[i],0};
fftb[i]=(E){B[i],0};
omg[i]=(E){cos(pi*2*i/n),sin(pi*2*i/n)};
}
FFT(ffta,fftc,0,n,1);
FFT(fftb,ffta,0,n,1);
for(i=0;i<n;i++)
fftb[i]=ffta[i]*fftc[i];
for(i=0;i<=n;i++)
omg[i]=(E){cos(-pi*2*i/n),sin(-pi*2*i/n)};
FFT(fftb,fftc,0,n,1);
for(i=0;i<n;i++)
C[i]=(int)(fftc[i].s/n+0.5);
}
int A[N],B[N],C[N];
int ans[N],tmp[N],tmp2[N],tmp3[N];
int main()
{
/*freopen("triple8.in","r",stdin);
freopen("triple8.ans","w",stdout);*/
int n;
scanf("%d",&n);
int i,j,x;
for(i=1;i<=n;i++)
{
scanf("%d",&x);
A[x]=1;
B[2*x]=1;
C[3*x]=1;
}
AFFT(A,B,tmp3,40000);
AFFT(A,A,tmp,80000);
AFFT(A,tmp,tmp2,120000);
for(i=0;i<=120000;i++)
{
ans[i]=(tmp2[i]-3*tmp3[i]+2*C[i])/6+(tmp[i]-B[i])/2+A[i];
if(ans[i]>0) printf("%d %d\n",i,ans[i]);
}
}