BZOJ[3771]Triple 生成函數+容斥原理

傳送門ber~

構造出原序列的生成函數A ,它的三次方就是損失的方案數
可惜題目要求方案互不相同,這樣計算會有重複的方案出現
那我們可以容斥一發
A 表示所有物品選一個的生成函數,B 表示一次選倆的生成函數,C 表示一次選仨的生成函數
手動容斥一發
拿一個的方案數:A
拿兩個的方案數:(AAB)/2AA 就是帶重複的選兩個,重複一共有兩種:第一種是兩個一樣的拼到一起,減去B 就可以解決,還有一種就是將x+yy+x 算了兩次,除2 就OK
拿三個的方案數:(A33AB+2C)/6 ,這個和上一個差不多,感性理解下就可以了(實在不行畫個圖模擬一下乘法的過程)

代碼如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<cmath>
#define N 550050
using namespace std;
const double DFT=2.0,IDFT=-2.0;
const double pi=acos(-1);
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
struct Complex{
    double x,y;
    Complex(){}
    Complex(double _,double __):x(_),y(__){}
    Complex operator + (Complex b) const{return Complex(x+b.x,y+b.y);}
    Complex operator - (Complex b) const{return Complex(x-b.x,y-b.y);}
    Complex operator * (Complex b) const{return Complex(x*b.x-y*b.y,x*b.y+y*b.x);}
    Complex operator * (double b) const{return Complex(x*b,y*b);}
    Complex operator / (double b) const{return Complex(x/b,y/b);}
}a[N],b[N],c[N],d[N];
int pos[N];
int n,x,len,maxx;
inline void FFT(Complex a[],double mode){
    for(int i=0;i<len;i++)
        if(i<pos[i])
            swap(a[i],a[pos[i]]);
    for(int i=2,mid=1;i<=len;i<<=1,mid<<=1){
        Complex wm(cos(2.0*pi/i),sin(mode*pi/i));
        for(int j=0;j<len;j+=i){
            Complex w(1,0);
            for(int k=j;k<j+mid;k++,w=w*wm){
                Complex l=a[k],r=w*a[k+mid];
                a[k]=l+r;a[k+mid]=l-r;
            }
        }
    }
    if(mode==IDFT)
        for(int i=0;i<len;i++)
            a[i].x/=len;
    return;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        x=read();
        a[x].x++;b[x*2].x++;c[x*3].x++;
        maxx=max(maxx,x*3);
    }
    for(len=1;len<maxx<<1;len<<=1);
    for(int i=0;i<len;i++){
        pos[i]=pos[i>>1]>>1;
        if(i&1) pos[i]|=len>>1;
    }
    FFT(a,DFT);FFT(b,DFT);FFT(c,DFT);
    for(int i=0;i<len;i++)
        d[i]=a[i]+(a[i]*a[i]-b[i])/2+(a[i]*a[i]*a[i]-a[i]*b[i]*3+c[i]*2)/6;
    FFT(d,IDFT);
    for(int i=1;i<=len;i++){
        int k=int(d[i].x+0.1);
        if(k) printf("%d %d\n",i,k);
    }
return 0;
}
發佈了165 篇原創文章 · 獲贊 132 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章