【SPOJ Triple Sums】FFT+容斥原理

感谢HDHD大神先提供了bzoj3513,只是那道题卡常至今没过也不想过了owo。

今天很开心owo。(为什么开心不说qwq)

SPOJ传送门

这道题如果没有加上三个数必须不同的限制,那么这道题就太简单过分了,直接把每个数映射的值+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);
	}
}

 

 

posted @ 2018-05-05 14:00  Newuser233  阅读(6)  评论(0)    收藏  举报