bzoj3771: Triple
码完这题有一种很爽很放松的感觉。。。155行。。。2.5个中午+1个早上真的值,毕竟我没mod题解啊!!!
这题一开始想的时候就想到了补0价值的斧头,然后FFT三次,结果发现有什么重复取一个啊,取的顺序啊牵扯出容斥啊,组合数什么的非常尴尬,然后一个半中午就玩没了,然后决定分开做,因为0太难搞了,可以视成很多种情况。
然后我就发现取一个和两个我很容易就可以搞,就是补0价值的斧头之后,FFT一次,然后可以发现,就有取a斧头和b斧头的顺序问题,那就除一下2
那么问题就转化为取三个斧头的情况了。
那就先自乘三次,就得到了斧头无限,顺序不同也算不同方案的方案数
首先先解决取了相同斧头的问题,那么ans在这个意义上是第1,2,3取的都不一样的方案 - 3*第1,2,3取的有两个一样的方案 + 2*第1,2,3取的有三个一样的方案(可能有人不理解这个方案为什么要*2,是因为比如取的是6,6,6,那它减了三次,实际上只需要减两次就好了。)
然后三个都一样的可以预处理吧,两个一样的就再跑一个FFT,怎么跑?想象一下,我们把两个相同的斧头捆在一起,然后让它和柿子再乘一次,就可以得到两个一样的情况。
接着就是顺序的问题了,三个都一样的没有这个问题不管ta,
有个栗子(打表推的):
比如4 5 6 8 ,得到18有10种
10:(5,5),(4,6),(6,4) 再+8
12:(6,6),(4,8),(8,4) 再+6
13:(5,8),(8,5) 再+5
14:(6,8),(8,6) 再+4
两个一样的情况就是(5,5,8)和(6,6,6)
分析一下,两个一样的情况有三种不同的组合,那就减掉
剩下就是都不一样的了,很容易发现有6种组合,那除一下,再把一样的加上
最后答案就是1/2的方案加3的方案了~
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; const double pi=acos(-1.0); struct Complex { double r,i; Complex(){r=0, i=0;} Complex(double _r,double _i){r=_r, i=_i;} friend Complex operator +(Complex x,Complex y){return Complex(x.r+y.r,x.i+y.i);} friend Complex operator -(Complex x,Complex y){return Complex(x.r-y.r,x.i-y.i);} friend Complex operator *(Complex x,Complex y){return Complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);} }T[410000],A[410000],B[410000]; int d[410000],d2[410000],len; int R[410000]; void fft(Complex *a,int n,int op) { for(int i=0;i<n;i++) if(i<R[i])swap(a[i],a[R[i]]); for(int i=1;i<n;i*=2) { Complex wn(cos(pi/i),sin(pi*op/i)); for(int j=0;j<n;j+=(i<<1)) { Complex w(1,0); for(int k=0;k<i;k++,w=w*wn) { Complex a1=a[j+k],a2=a[j+k+i]; a[j+k] =a1+w*a2; a[j+k+i]=a1-w*a2; } } } } void FFT1(int n,int m) { int nn=n,L=0; for(int i=0;i<=n;i++)A[i].r=B[i].r=T[i].r; m=m+n;for(n=1;n<=m;n*=2)L++; for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); fft(A,n,1);fft(B,n,1); for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0; fft(A,n,-1); for(int i=0;i<n;i++) A[i].r=int(A[i].r/n+0.5), A[i].i=0; for(int i=1;i<n;i++)A[i].r/=2; for(int i=0;i<n;i++)d[i]+=int(A[i].r), A[i].r=0, A[i].i=0; len=m; //第一次FFT,解决用1/2个不同物品组成不同价值 } void FFT2(int n,int m) { int nn=n,L=0; for(int i=0;i<=n;i++)A[i].r=B[i].r=T[i].r; m=m+n;for(n=1;n<=m;n*=2)L++; for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); fft(A,n,1);fft(B,n,1); for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0; fft(A,n,-1); for(int i=0;i<n;i++) { A[i].r=int(A[i].r/n+0.5), A[i].i=0; // if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r)); } // printf("\n"); n=nn;L=0; for(int i=0;i<=n;i++)B[i].r=T[i].r; m=m+n;for(n=1;n<=m;n*=2)L++; for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); fft(A,n,1);fft(B,n,1); for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0; fft(A,n,-1); for(int i=0;i<n;i++) { A[i].r=int(A[i].r/n+0.5), A[i].i=0; // if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r)); } // printf("\n"); for(int i=0;i<n;i++)d2[i]+=int(A[i].r), A[i].r=0, A[i].i=0; len=m; // for(int i=0;i<=len;i++) // if(d[i]!=0)printf("%d %d\n",i,d[i]); } void FFT3(int n,int m) { int nn=n,L=0; for(int i=0;i<=n;i++)A[i+i].r=B[i].r=T[i].r; m=m+n;for(n=1;n<=m;n*=2)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++) if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r)); for(int i=0;i<n;i++) if(int(B[i].r)!=0)printf("%d %d\n",i,int(B[i].r)); */ fft(A,n,1);fft(B,n,1); for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0; fft(A,n,-1); for(int i=0;i<n;i++) A[i].r=int(A[i].r/n+0.5), A[i].i=0; // for(int i=0;i<n;i++) // if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r)); for(int i=0;i<n;i++)d2[i]-=int(A[i].r)*3, A[i].r=0, A[i].i=0; for(int i=1;i<=len;i++) { d2[i]/=6;d2[i]+=d[i]; if(d2[i]!=0) printf("%d %d\n",i,d2[i]); } } int main() { // freopen("f.in","r",stdin); // freopen("f.out","w",stdout); int n,x,mx=0; scanf("%d",&n); memset(d,0,sizeof(d)); memset(d2,0,sizeof(d2)); for(int i=1;i<=n;i++) { scanf("%d",&x); T[x].r++;d2[x+x+x]+=2; mx=max(mx,x); } T[0].r=1;FFT1(mx,mx); T[0].r=0;FFT2(mx,mx); FFT3(mx*2,mx); return 0; }
pain and happy in the cruel world.