【SPOJ Triple Sums】FFT+容斥原理
感谢HDHD大神先提供了bzoj3513,只是那道题卡常至今没过也不想过了owo。
今天很开心owo。(为什么开心不说qwq)
这道题如果没有加上三个数必须不同的限制,那么这道题就太简单过分了,直接把每个数映射的值+1(如这个数为4,那么数组a[4]++),然后直接DFT后乘个立方然后再IDFT,最后得到的系数就是答案。
但是加上了限制其实也就是一个容斥的事情。原本不加限制求到的是segma(x)^3
由于选三个出来可能是AAA,AAB,ABA,BAA的情况
现在 [latex]ans = ( \Sigma(x)^3 - 3 * \Sigma(x^2 ) * \Sigma(x) + 2*\Sigma(x^3) ) /(3!)[/latex]
这个的意义就是所有种类减两次取到同一数的3种情况, 再加上三次取到同一数的情况(容斥),由于我们是组合最后除一个3!即6。
还有个要注意的地方要将整个计算过程往上平移20000个单位。
最后用FFT优化多项式系数乘法就搞定了!
code:
#include<stdio.h> #include<complex> #include<cstdio> #include<cmath> #include<cstring> using namespace std; const int maxn = 200000; const double pi = 3.1415926535; typedef complex<double> cd; cd aa[maxn],bb[maxn],cc[maxn]; int rev[maxn],out[maxn]; void getrev(int n) { for(int i=0,j=0;i<n;i++) { rev[i]=j; for(int k=n>>1;(j^=k)<k;k>>=1); } } void fft(cd *a,int n,int dft) { for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]); for(int st=1;st<n;st<<=1) { cd dwfg=exp(cd(0,dft*pi/st)); for(int i=0;i<n;i+=(st<<1)) { cd nfg=1; for(int j=i;j<i+st;j++) { cd x=a[j],y=a[j+st]*nfg; nfg*=dwfg; a[j]=x+y; a[j+st]=x-y; } } } if(dft==-1) for(int i=0;i<n;i++) a[i]/=n; } int n; int t1[maxn],t2[maxn],t3[maxn]; int main() { int mx=0; int x; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); x+=20000; t1[x]+=1; t2[x*2]+=1; t3[x*3]+=1; mx=max(mx,x); } int s=2; for(;s<=mx*3;s<<=1); getrev(s); for(int i=0;i<=mx;i++) aa[i]=t1[i]; for(int i=0;i<=mx*2;i++) bb[i]=t2[i]; fft(aa,s,1); fft(bb,s,1); for(int i=0;i<s;i++) aa[i]=aa[i]*( aa[i]*aa[i] - 3.0 * bb[i] ); fft(aa,s,-1); for(int i=0;i<s;i++) { x = (int)floor((aa[i].real() +2.0*t3[i])/6.0+0.5); if(x>0) printf("%d %d\n",i-60000,x); } }