1. 程式人生 > >[BZOJ3684][拉格朗日反演+多項式求冪]大朋友和多叉樹

[BZOJ3684][拉格朗日反演+多項式求冪]大朋友和多叉樹

需要 技術 一行 我們 while ref text ldo 多項式求逆

題面

Description

我們的大朋友很喜歡計算機科學,而且尤其喜歡多叉樹。對於一棵帶有正整數點權的有根多叉樹,如果它滿足這樣的性質,我們的大朋友就會將其稱作神犇的:點權為\(1\)的結點是葉子結點;對於任一點權大於\(1\)的結點\(u\)\(u\)的孩子數目\(deg_u\)屬於集合\(D\),且\(u\)的點權等於這些孩子結點的點權之和。

給出一個整數\(s\),你能求出根節點權值為\(s\)的神犇多叉樹的個數嗎?請參照樣例以更好的理解什麽樣的兩棵多叉樹會被視為不同的。

我們只需要知道答案關於\(950009857\)\(453\times2^{21}+1\),一個質數)取模後的值。

Input

第一行有\(2\)個整數\(s,m\)

第二行有\(m\)個互異的整數,\(d_1,d_2,\ldots,d_m\),為集合\(D\)中的元素。

Output

輸出一行僅一個整數,表示答案模\(950009857\)的值。

Sample Input

4 2
2 3

Sample Output

10

HINT

技術分享圖片
\(1\le m\le s\le10^5,2\le d_i\le s\),有\(3\)組小數據和\(3\)組大數據。

分析

首先,我們設\(v_i\)為集合\(D\)中是否有\(i\)這個元素,有則為\(1\),無則為\(0\)\[v_i=\sum_{k=1}^{m}[d_k=i]\]

我們令\(V(x)\)

\(v_i\)的生成函數:\[V(x)=\sum_{k=0}^\infty v_i x^k\]

我們再設\(f_i\)為根節點權值為\(i\)的神犇多叉樹的數量。

首先,顯然有\(f_0=0\);而當\(i=1\)即葉子節點時,有\(f_1=1\)

而在\(i>1\)時,我們枚舉根節點的兒子節點數量,在枚舉各個葉子節點的權值,根據乘法原理得到遞歸式:\[f_i=\sum_{k=0}^{i-1}v_k\sum_{s_1+s_2+\cdots+s_k=i}\;\;\prod_{j=1}^k f_j\]

我們發現後面這個是一個\(k\)重卷積。那麽我們令\(F(x)\)\(f_i\)的生成函數:\[F(x)=\sum_{k=0}^\infty f_k x^k\]

那麽我們根據遞歸式再加上特殊情況時的值,可以得到:\[F(x)=\sum_{k=0}^\infty v_k F(x)^k+x\]

那麽我們發現這就是\(V(x)\)的形式。那麽我們就得到:\[F(x)=V(F(x))+x\]

\[F(x)-V(F(x))=x\]

只要我們令\(G(x)=x-V(x)\),就可以構造出一個拉格朗日反演的形式:\[G(F(x))=x\]

那麽我們作反演就得到:\[[x^n]F(x)=\frac{1}{n}[x^{n-1}]\left(\frac{x}{G(x)}\right)^n\]

即:\[f_s=\frac{1}{s}[x^{s-1}]\left(\frac{x}{G(x)}\right)^s\]

註意到我們可以\(x\)\(G(x)\)約掉一個\(x\),則我們令:\[H(x)=\sum_{k=0}^\infty v_{k+1} x^k=\frac{C(x)}{x}\]

則有:\[f_s=\frac{1}{s}[x^{s-1}]\left(\frac{1}{1-H(x)}\right)^n\]

我們直接多項式求逆+多項式求冪就可以解決了。

關於拉格朗日反演、多項式的操作,詳見我的這篇博客:多項式全家桶

代碼

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll p=950009857,g=7;
int n,nn,s,m,r[262145];
ll inv[262145],c[262145],gn[2][262145],ans;
inline ll pow(ll a,int b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
inline ll add(ll a,ll b){return a+b>p?a+b-p:a+b;}
inline ll cut(ll a,ll b){return a-b<0?a-b+p:a-b;}
void init(){
    for(n=1;n<=s;n<<=1);
    nn=n;
    gn[0][0]=gn[1][0]=1;
    gn[0][1]=pow(g,(p-1)/(n<<1));
    gn[1][1]=pow(gn[0][1],p-2);
    for(int i=2;i<(n<<1);i++){gn[0][i]=gn[0][i-1]*gn[0][1]%p;gn[1][i]=gn[1][i-1]*gn[1][1]%p;}
    inv[1]=1;
    for(int i=2;i<=(n<<1);i++)inv[i]=inv[p%i]*(p-p/i)%p;
}
void NTT(ll c[],int n,int tp=1){
    for(int i=0;i<n;i++){
        r[i]=(r[i>>1]>>1)|((i&1)*(n>>1));
        if(i<r[i])swap(c[i],c[r[i]]);
    }
    for(int i=1;i<n;i<<=1){
        for(int j=0;j<n;j+=(i<<1)){
            for(int k=0;k<i;k++){
                ll x=c[j+k],y=gn[tp!=1][nn/i*k]*c[j+k+i]%p;
                c[j+k]=add(x,y);
                c[j+k+i]=cut(x,y);
            }
        }
    }
}
void INTT(ll c[],int n){
    NTT(c,n,-1);
    for(int i=0;i<n;i++)c[i]=c[i]*inv[n]%p;
}
void inverse(ll c[],int n=n){
    static ll t[262145],tma[262145];
    t[0]=pow(c[0],p-2);
    for(int k=2;k<=n;k<<=1){
        for(int i=0;i<(k<<1);i++)tma[i]=(i<k?c[i]:0);
        for(int i=(k>>1);i<(k<<1);i++)t[i]=0;
        NTT(tma,k<<1);
        NTT(t,k<<1);
        for(int i=0;i<(k<<1);i++)t[i]=cut(add(t[i],t[i]),t[i]*t[i]%p*tma[i]%p);
        INTT(t,k<<1);
    }
    memcpy(c,t,sizeof(ll)*n);
}
void derivative(ll c[],int n=n){for(int i=0;i<n;i++)c[i]=c[i+1]*(i+1)%p;}
void integrate(ll c[],int n=n){for(int i=n-1;i>=1;i--)c[i]=c[i-1]*inv[i]%p;c[0]=0;}
void ln(ll c[],int n=n){
    static ll t[262145];
    for(int i=0;i<(n<<1);i++)t[i]=(i<n?c[i]:0);
    derivative(t,n);
    inverse(c,n);
    NTT(t,n<<1);
    NTT(c,n<<1);
    for(int i=0;i<(n<<1);i++)c[i]=c[i]*t[i]%p;
    INTT(c,n<<1);
    for(int i=n;i<(n<<1);i++)c[i]=0;
    integrate(c,n);
}
void exp(ll c[]){
    static ll t[262145],ta[262145];
    t[0]=1;
    for(int k=2;k<=n;k<<=1){
        for(int i=0;i<(k<<1);i++)ta[i]=t[i];
        ln(ta,k);
        for(int i=0;i<k;i++)ta[i]=cut(c[i],ta[i]);
        ta[0]++;
        NTT(t,k<<1);
        NTT(ta,k<<1);
        for(int i=0;i<(k<<1);i++)t[i]=t[i]*ta[i]%p;
        INTT(t,k<<1);
        for(int i=k;i<(k<<1);i++)t[i]=0;
    }
    memcpy(c,t,sizeof(ll)*n);
}
void pow(ll c[],int k){
    ln(c);
    for(int i=0;i<n;i++)c[i]=c[i]*k%p;
    exp(c);
}
int main(){
    scanf("%d%d",&s,&m);
    for(int i=1;i<=m;i++){
        int x;
        scanf("%d",&x);
        c[x-1]=p-1;
    }
    c[0]=1;
    init();
    inverse(c);
    pow(c,s);
    printf("%lld\n",c[s-1]*inv[s]%p);
}

[BZOJ3684][拉格朗日反演+多項式求冪]大朋友和多叉樹