SPOJ -- Triple Sums(FFT)
2015-08-09 00:45:56
【传送门】
题意:给出最多40000个数v1~vn,每个数在-20000~20000之间,任取三个数(每个数不能重复取)求和,问所有可能组成的和,以及其组成方案数。
思路:先把数域转化到0 ~ 40000内,然后统计每个数出现次数,得到系数多项式A。
对于其自卷积两次后,会有形如:,三种形式。
我们需要的是第一种,考虑用容斥去掉后面两种。
构造以下卷积:
那么答案就为:
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = (1 << 18) + 10; const double DPI = 2.0 * acos(-1.0); int n,n3,N,bit; int rev[MAXN]; struct CP{ //复数类 double a,b; CP(double ta = 0,double tb = 0) : a(ta) , b(tb) {} }A1[MAXN],A2[MAXN],B[MAXN],C[MAXN]; inline CP operator * (CP &a,CP &b){return CP(a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a);} inline CP operator + (CP &a,CP &b){return CP(a.a + b.a,a.b + b.b);} inline CP operator - (CP &a,CP &b){return CP(a.a - b.a,a.b - b.b);} void Pre_cal(){ n3 = 120010; //结果多项式次数界 memset(rev,0,sizeof(rev)); for(N = 1,bit = 0; N < n3; N <<= 1,++bit); for(int i = 1; i < N; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1)); } void FFT(CP *A,int n,int f){ for(int i = 0; i < n; ++i) if(i < rev[i]) swap(A[i],A[rev[i]]); for(int m = 2; m <= n; m <<= 1){ //DFT结果的项数 CP wm(cos(DPI / m),f * sin(DPI / m)); //m次单位复数根 for(int k = 0; k < n; k += m){ //遍历每一块 CP w(1,0); for(int j = k; j < k + (m >> 1); ++j){ //折半,关键 CP t = w * A[j + (m >> 1)]; //右项 CP u = A[j]; //左项 A[j] = u + t; A[j + (m >> 1)] = u - t; w = w * wm; //更新w } } } if(f == -1) for(int i = 0; i < n; ++i) A[i].a /= n; } void Solve(){ //流程:倍次,求值,乘法,插值 Pre_cal(); FFT(A1,N,1); //求值1 FFT(A2,N,1); //求值2 for(int i = 0; i < N; ++i){ A1[i] = A1[i] * A2[i]; A1[i] = A1[i] * A2[i]; } FFT(A1,N,-1); FFT(B,N,1); for(int i = 0; i < N; ++i) B[i] = B[i] * A2[i]; FFT(B,N,-1); for(int i = 0; i < n3; ++i) if(A1[i].a != 0){ ll ans = (ll)(A1[i].a + 0.5) - 3LL * (ll)(B[i].a + 0.5) + 2LL * (ll)(C[i].a + 0.5); if(ans > 0) printf("%d : %lld\n",i - 60000,ans / 6LL); } } int main(){ int a; scanf("%d",&n); for(int i = 0; i < n; ++i){ scanf("%d",&a); a += 20000; A1[a].a += 1.0; A2[a].a += 1.0; B[2 * a].a += 1.0; C[3 * a].a += 1.0; } Solve(); return 0; }