BZOJ 3771: Triple(生成函數+FFT+容斥)

題目描述

傳送門

題目大意:給一堆不同的數,問你從中取1-3個組成不同和的方案數,不考慮順序。每個數的大小<=40000。


思路

簡單的容斥。搞出取一個的生成函數A 、取兩個相同的生成函數B 、取三個相同的生成函數C ,然後答案就是A+A2B+A33AB+2C

乘起來的過程用FFT加速就行了。這是考慮了順序的,不考慮順序的話在加上的時候除以對應的階乘就行了。

ps:一開始次數界搞成了非二的冪,死活找不到錯的感覺。


代碼

#include <bits/stdc++.h>
#define MAXN 120010

using namespace std;

typedef long long LL;
const double PI = acos(-1.0);
int n;

struct Complex{
    double real, image;
    Complex() {}
    Complex(double _real, double _image){
        real = _real;
        image = _image;
    }
    friend Complex operator + (Complex A, Complex B){
        return Complex(A.real + B.real, A.image + B.image);
    }
    friend Complex operator - (Complex A, Complex B){
        return Complex(A.real - B.real, A.image - B.image);
    }
    friend Complex operator * (Complex A, Complex B){
        return Complex(A.real * B.real - A.image * B.image, A.real * B.image + A.image * B.real);
    }
}a[MAXN<<2], b[MAXN<<2], c[MAXN<<2], d[MAXN<<2];

LL ans[MAXN<<2];

int Get(int x){
    int t = 1;
    while(t < x)  t <<= 1;
    t <<= 1;
    return t;
}

void Reverse(Complex *A){
    for(int i = 0; i < n-1; i++){
        int j = 0;
        for(int k = 1, tmp = i; k < n; k <<= 1, tmp >>= 1)
            j = ((j << 1) | (tmp & 1));
        if(j > i)  swap(A[i], A[j]); 
    }
}

void FFT(Complex *A, int DFT){
    Reverse(A);
    for(int s = 1; (1 << s) <= n; s++){
        int m = 1 << s;
        Complex wn = Complex(cos(2*PI*DFT/m), sin(2*PI*DFT/m));
        for(int k = 0; k < n; k += m){
            Complex w = Complex(1, 0);
            for(int j = 0; j < (m>>1); j++){
                Complex u = A[k+j], t = w * A[k+j+(m>>1)];          
                A[k+j] = u + t;
                A[k+j+(m>>1)] = u - t;
                w = w * wn;
            }
        }
    }
    if(DFT == -1)  for(int i = 0; i < n; i++)  A[i].real /= n;
}

int main(){

    scanf("%d", &n);

    int Max = 0, x;
    for(int i = 1; i <= n; i++){
        scanf("%d", &x);
        a[x].real += 1.0;
        b[x+x].real += 1.0;
        c[x+x+x].real += 1.0;
        Max = max(Max, x);
    }

    Max *= 3;
    n = Get(Max+2);

    for(int i = 0; i < n; i++)  ans[i] += (LL)(a[i].real + .5);

    FFT(a, 1);  for(int i = 0; i < n; i++)  d[i] = a[i] * a[i];  FFT(d, -1);

    for(int i = 0; i < n; i++)  ans[i] += ((LL) (d[i].real + .5) - (LL)(b[i].real + .5)) / 2;

    for(int i = 0; i < n; i++)  d[i] = a[i] * a[i] * a[i];  FFT(d, -1);
    FFT(b, 1);  for(int i = 0; i < n; i++)  b[i] = a[i] * b[i];  FFT(b, -1);

    for(int i = 0; i < n; i++)  
        ans[i] += ((LL)(d[i].real + .5) - 3 * (LL)(b[i].real + .5) + 2 * (LL)(c[i].real + .5)) / 6;

    for(int i = 0; i <= Max; i++)
        if(ans[i])  printf("%d %lld\n", i, ans[i]);

    return 0;
}

此去泉臺招舊部,旌旗十萬斬閻羅。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章