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;
}

 

posted @ 2015-08-09 00:57  Naturain  阅读(141)  评论(0编辑  收藏  举报