[SPOJ TSUM] Triple Sums [FFT]

8 篇文章 0 订阅
1 篇文章 0 订阅

给一些数,从中选取3个,问他们的和。输出对于每个和,能够组成这个和的方案数。

快速傅立叶变换...第i项的系数为值为i的数出现了多少次,然后计算这个多项式的三次方,再用容斥原理去重即可。用的别人的模板..

SPOJ跑的是真慢...常数大一点都过不去...只好先DFT,然后计算,最后再进行逆变换..

另外FFT丢精度感觉丢的挺多的...本机跑样例,不除6的话,都过不去...

#include <complex>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstdio>

using namespace std;

typedef complex <long double> Complex;

const long double PI = acos(-1.);
const int N = 1 << 17;
const long double three = 3.0;

void FFT(Complex* P, int n, int oper) {
    for (int i = 1, j = 0; i < n - 1; i++) {
        for (int s = n; j ^= s >>= 1, ~j & s;);
        if (i < j) {
            swap(P[i], P[j]);
        }
    }
    Complex unit_p0;
    for (int d = 0; (1 << d) < n; d++) {
        int m = 1 << d, m2 = m * 2;
        long double p0 = PI / m * oper;
        unit_p0 = Complex(cos(p0), sin(p0));
        for (int i = 0; i < n; i += m2) {
            Complex unit = 1;
            for (int j = 0; j < m; j++) {
                Complex &P1 = P[i + j + m], &P2 = P[i + j];
                Complex t = unit * P1;
                P1 = P2 - t;
                P2 = P2 + t;
                unit = unit * unit_p0;
            }
        }
    }
}

int a[N]={0};
int b[N]={0};
int c[N]={0};
Complex A[N],B[N],AB[N];

int main() {
	int n,x,i;
	scanf("%d",&n);
	for (i=0;i<n;i++) {
		scanf("%d",&x);
		a[x+20000]++;
		b[x+x+40000]++;
		c[x+x+x+60000]++;
	}
	for (i=0;i<N;i++) {
		A[i]=a[i];
		B[i]=b[i];
	}
	FFT(A,N,1);
	FFT(B,N,1);
	for (i=0;i<N;i++) {
		Complex &tmp=A[i];
		AB[i]=tmp*(tmp*tmp-three*B[i]);
	}
	FFT(AB,N,-1);
	for (i=0;i<N;i++) {
		long long ans=((long long)(AB[i].real()/N+0.5)+2*c[i])/6;
		if (ans!=0) printf("%d : %lld\n",i-60000,ans);
	}
	return 0;
}

附上模板...

#include <complex>
#include <algorithm>
#include <cmath>
#include <vector>

using namespace std;

typedef complex <double> Complex;
typedef vector <int> Polynomial;

const double PI = acos(-1.);
const int N = 1 << 17;

void FFT(Complex* P, int n, int oper) {
	for (int i = 1, j = 0; i < n - 1; i++) {
		for (int s = n; j ^= s >>= 1, ~j & s;);
		if (i < j) {
			swap(P[i], P[j]);
		}
	}
	Complex unit_p0;
	for (int d = 0; (1 << d) < n; d++) {
		int m = 1 << d, m2 = m * 2;
		double p0 = PI / m * oper;
		unit_p0 = Complex(cos(p0), sin(p0));
		for (int i = 0; i < n; i += m2) {
			Complex unit = 1;
			for (int j = 0; j < m; j++) {
				Complex &P1 = P[i + j + m], &P2 = P[i + j];
				Complex t = unit * P1;
				P1 = P2 - t;
				P2 = P2 + t;
				unit = unit * unit_p0;
			}
		}
	}
}

Complex A[N], B[N];

Polynomial operator * (const Polynomial &u, const Polynomial &v)
{
	int n=1,p=u.size(),q=v.size(),i;
	while (n<=p+q-2) n<<=1;
	for (i=0;i<n;++i) A[i]=i<p?u[i]:0;
	for (i=0;i<n;++i) B[i]=i<q?v[i]:0;
	FFT(A,n,1);
	FFT(B,n,1);
	for (i=0;i<n;++i) A[i]*=B[i];
	FFT(A,n,-1);
	Polynomial w(p+q-1);
	for (i=0;i<w.size();++i) 
		w[i]=(int)(A[i].real()/n+0.5);
	return w;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值