zoukankan      html  css  js  c++  java
  • 【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
    ",1LL*a[s-1]*inv[s]%mod);
        return 0;
    }
  • 相关阅读:
    Windows、Linux、ARM、Android、iOS全平台支持的RTMP推流组件EasyRTMPAndroid如何修改分辨率和码率
    高稳定性、低延时的网络全终端播放器H5播放器网页直播/点播播放器EasyPlayer.js播放flv格式视频显示跨域问题解决方案
    RTSP播放器网页web无插件直播流媒体音视频播放器EasyPlayerRTSP实现支持H265编码格式过程介绍
    简单、高效、易用的全平台(Windows/Linux/ARM/Android/iOS)web实现RTMP推送组件EasyRTMPAndroid如何开启悬浮窗
    模板学习实践一 accumulationtraits
    模板学习实践二 pointer
    黑客屏保 代码来自网络搜索 做了部分改动
    设计模式之模板模式 template
    CMAKE 教程前两章节学习
    cmake 及make 实践记录
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476834.html
Copyright © 2011-2022 走看看