【BZOJ3684】大朋友和多叉树 【拉格朗日反演】【FFT/NTT】

题解
其实就是一个拉格朗日反演+多项式全家桶。
拉格朗日反演:如果两个多项式f,g满足g(f(x))=x,则有

[xn]f(x)=1n[x1]1g(x)n

我们更常用的形式是
[xn]f(x)=1n[xn1]1g(x)n

其中g(x)=g(x)/x。因此我们需要钦点g没有常数项。
在这题内,我们设f(x)为点权为x的方案总数。则答案为f(s)
我们设F(x)=if(i)。则有
F(x)=x+kDF(x)k

就是自己一个点一棵树的情况加上选若干个儿子的情况。
移项得到
F(x)kDF(x)k=x

我们设G(x)=xkDxk
则满足G(F(x))=x
拉格朗日反演就好了。
这里的多项式快速幂,我们可以先求ln,每一项都乘一下,再求exp。快速幂之后再求个逆。
全部操作都是迭代版本的。
时间复杂度O(nlog2n)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=270005,mod=950009857;
int s,n,m,x,a[N],inv[N],rev[N];
int fastpow(int a,int x){
    int res=1;
    while(x){
        if(x&1){
            res=1LL*res*a%mod;
        }
        x>>=1;
        a=1LL*a*a%mod;
    }
    return res;
}
void ntt(int a[],int n,int dft){
    for(int i=0;i<n;i++){
        rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
        if(i<rev[i]){
            swap(a[i],a[rev[i]]);
        }
    }
    for(int i=1;i<n;i<<=1){
        int wn=fastpow(7,(mod-1)/i/2);
        if(dft==-1){
            wn=fastpow(wn,mod-2);
        }
        for(int j=0;j<n;j+=(i<<1)){
            int w=1,x,y;
            for(int k=j;k<j+i;k++,w=1LL*w*wn%mod){
                x=a[k];
                y=1LL*w*a[k+i]%mod;
                a[k]=x+y<mod?x+y:x+y-mod;
                a[k+i]=x-y>=0?x-y:x-y+mod;
            }
        }
    }
    if(dft==-1){
        int inv=fastpow(n,mod-2);
        for(int i=0;i<n;i++){
            a[i]=1LL*a[i]*inv%mod;
        }
    }
}
void inverse(int a[],int n){
    static int b[N],c[N],d[N];
    b[0]=fastpow(a[0],mod-2);
    for(int k=2;k<=n;k<<=1){
        for(int i=0;i<(k<<1);i++){
            if(i<k){
                c[i]=a[i];
            }else{
                c[i]=0;
            }
            if(i<(k>>1)){
                d[i]=b[i];
            }else{
                d[i]=0;
            }
        }
        ntt(c,k<<1,1);
        ntt(d,k<<1,1);
        for(int i=0;i<(k<<1);i++){
            c[i]=(2*d[i]%mod-1LL*c[i]*d[i]%mod*d[i]%mod+mod)%mod;
        }
        ntt(c,k<<1,-1);
        for(int i=0;i<k;i++){
            b[i]=c[i];
        }
    }
    for(int i=0;i<n;i++){
        a[i]=b[i];
    }
}
void dao(int a[],int n){
    for(int i=0;i<n-1;i++){
        a[i]=1LL*a[i+1]*(i+1)%mod;
    }
    a[n-1]=0;
}
void ji(int a[],int n){
    for(int i=n-1;i>0;i--){
        a[i]=1LL*a[i-1]*inv[i]%mod;
    }
    a[0]=0;
}
void getln(int a[],int n){
    static int b[N];
    for(int i=0;i<(n<<1);i++){
        if(i<n){
            b[i]=a[i];
        }else{
            b[i]=a[i]=0;
        }
    }
    dao(a,n<<1);
    inverse(b,n);
    ntt(a,n<<1,1);
    ntt(b,n<<1,1);
    for(int i=0;i<(n<<1);i++){
        a[i]=1LL*a[i]*b[i]%mod;
    }
    ntt(a,n<<1,-1);
    ji(a,n<<1);
}
void getexp(int a[],int n){
    static int b[N],c[N];
    b[0]=1;
    for(int k=2;k<=n;k<<=1){
        for(int i=0;i<(k<<1);i++){
            if(i<(k>>1)){
                c[i]=b[i];
            }else{
                c[i]=0;
            }
        }
        getln(c,k);
        for(int i=0;i<k;i++){
            c[i]=(a[i]+(i==0)+mod-c[i])%mod;
        }
        ntt(c,k<<1,1);
        ntt(b,k<<1,1);
        for(int i=0;i<(k<<1);i++){
            c[i]=1LL*c[i]*b[i]%mod;
        }
        ntt(c,k<<1,-1);
        ntt(b,k<<1,-1);
        for(int i=0;i<k;i++){
            b[i]=c[i];
        }
    }
    for(int i=0;i<n;i++){
        a[i]=b[i];
    }
}
int main(){
    scanf("%d%d",&s,&m);
    a[0]=1;
    for(int i=1;i<=m;i++){
        scanf("%d",&x);
        a[x-1]=(a[x-1]+mod-1)%mod;
    }
    for(n=1;n<=s;n<<=1);
    inv[1]=1;
    for(int i=2;i<n;i++){
        inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
    }
    getln(a,n);
    for(int i=0;i<n;i++){
        a[i]=1LL*a[i]*s%mod;
    }
    getexp(a,n);
    inverse(a,n);
    printf("%lld\n",1LL*a[s-1]*inv[s]%mod);
    return 0;
}
posted @ 2018-08-09 22:05  ez_2016gdgzoi471  阅读(240)  评论(0编辑  收藏  举报