【普通型母函數+容斥+FFT】BZOJ3771[Triple]題解

題目概述

ZigZagK有 n 把價值不一樣的斧子,CHNJZ偷走了 1 把或 2 把或 3 把,對於每個可能的總損失,計算有幾種可能的方案。

解題報告

emm……顯然是母函數啊?但是有數量限制。由於最多偷走三把,所以我們可以直接三種情況都討論過去。

1 把:母函數:A(x)=xa1+xa2+xa3++xan

2 把:母函數:A2(x) ,但是這樣顯然算進了同把斧子用了兩次的情況,所以要去掉 B(x)=x2a1+x2a2+x2a3++x2an 。又因爲這樣算進了排列,所以最後要除以 2

3 把:母函數:A3(x) ,去掉 3A(x)B(x) ,補回 2[C(x)=x3a1+x3a2+x3a3++x3an] ,除以 6

因爲項數比較多,所以用FFT進行多項式乘法。

解題報告

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fr first
#define sc second
#define mp make_pair
using namespace std;
typedef long double DB;typedef pair<DB,DB> CN;
const int maxn=262144;const DB pi=acos(-1);

int n,a[maxn+5],MAX,R[maxn+5],ans[maxn+5];
CN A[maxn+5],B[maxn+5],C[maxn+5],now[maxn+5];

inline void Rev(int n){
    int len=-1;for (int x=1;x<n;x<<=1) len++;R[0]=0;
    for (int i=1;i<n;i++) R[i]=(R[i>>1]>>1)|((i&1)<<len);
}
inline CN operator + (const CN &a,const CN &b) {return mp(a.fr+b.fr,a.sc+b.sc);}
inline CN operator - (const CN &a,const CN &b) {return mp(a.fr-b.fr,a.sc-b.sc);}
inline CN operator * (const CN &a,const CN &b) {return mp(a.fr*b.fr-a.sc*b.sc,a.fr*b.sc+a.sc*b.fr);}
inline void FFT(CN *a,int n,int f){
    for (int i=0;i<n;i++) if (i<R[i]) swap(a[i],a[R[i]]);
    for (int k=1;k<n;k<<=1){
        CN w=mp(1,0),wn=mp(cos(pi/k),sin(f*pi/k)),x,y;
        for (int i=0;i<n;i+=k<<1,w=mp(1,0))
            for (int j=0;j<k;j++,w=w*wn)
                x=a[i+j],y=w*a[i+j+k],a[i+j]=x+y,a[i+j+k]=x-y;
    }
    if (f<0) for (int i=0;i<n;i++) a[i].fr/=n;
}
int main(){
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]),MAX=max(MAX,a[i]);
    for (int i=1;i<=n;i++) A[a[i]]=B[a[i]*2]=C[a[i]*3]=mp(1,0);MAX*=3;
    for (n=1;n<=(MAX<<1);n<<=1);Rev(n);for (int i=0;i<n;i++) ans[i]+=A[i].fr+0.1;
    FFT(A,n,1);for (int i=0;i<n;i++) now[i]=A[i]*A[i];FFT(now,n,-1);
    for (int i=0;i<n;i++) ans[i]+=(now[i].fr-B[i].fr)/2+0.1;
    FFT(B,n,1);for (int i=0;i<n;i++) B[i]=A[i]*B[i];FFT(B,n,-1);
    for (int i=0;i<n;i++) now[i]=A[i]*A[i]*A[i];FFT(now,n,-1);
    for (int i=0;i<n;i++) ans[i]+=(now[i].fr-B[i].fr*3+C[i].fr*2)/6+0.1;
    for (int i=0;i<=MAX;i++) if (ans[i]) printf("%d %d\n",i,ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章