• CF438E-The Child and Binary Tree【生成函数】


    正题

    题目链接:https://www.luogu.com.cn/problem/CF438E


    题目大意

    每个节点有(n)个权值可以选择,对于(1sim m)中的每个数字(k),求权值和为(k)的二叉树个数。


    解题思路

    (f_n)表示权值和为(n)的方案数,(g_n)表示(n)这个权值是否可用。
    那么我们对于一个(n)的转移,可以枚举根节点的权值,然后再用(f)去计算子节点的权值,具体的式子是

    [f_n=sum_{w=1}^ng_wsum_{i=0}^{n-w}f_if_{n-w-i} ]

    会发现这个三个数的下标和就是(n),这其实一个大卷积,设多项式(F[x]=f_x)(G[x]=g_x)。那么根据上面式子就有

    [F=F^2G+1 ]

    (加1是因为(f_0=1),然后可以解出式子)

    [F=frac{1pm sqrt{1-4G}}{2G} ]

    这里的(pm)我们取负号,因为取正号时不满足收敛性。

    当然我也不知道怎么判正负,但是这题发现(F[0]=1)但是(G[0]=1)。又有(2GF=1pmsqrt{1-4G}),显然有((2GF)[0]=0)。但是如果取正号,那么((1+sqrt{1-4G})[0]geq1),所以显然不可能,只能取负。

    然后可以直接上多项式开根和求逆做?发现(G[0]=0)不能求逆,只好再化一下式子变成

    [F=frac{2}{1+sqrt{1-4G}} ]

    时间复杂度(O(nlog n))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=8e5+10,P=998244353,inv2=(P+1)/2;
    ll n,m,a[N],b[N];
    ll power(ll x,ll b){
        ll ans=1;
        while(b){
            if(b&1)ans=ans*x%P;
            x=x*x%P;b>>=1;
        }
        return ans;
    }
    namespace Poly{
        ll n,t1[N],t2[N],t3[N],t4[N],r[N];
        void GetL(ll l){
            n=1;while(n<=l)n<<=1;
            for(ll i=0;i<n;i++)
                r[i]=(r[i>>1]>>1)|((i&1)?(n>>1):0);
            return;
        }
        void NTT(ll *f,ll op){
            for(ll i=0;i<n;i++)
                if(i<r[i])swap(f[i],f[r[i]]);
            for(ll p=2;p<=n;p<<=1){
                ll len=p>>1,tmp=power(3,(P-1)/p);
                if(op==-1)tmp=power(tmp,P-2);
                for(ll k=0;k<n;k+=p){
                    ll buf=1;
                    for(ll i=k;i<k+len;i++){
                        ll tt=f[i+len]*buf%P;
                        f[i+len]=(f[i]-tt+P)%P;
                        f[i]=(f[i]+tt)%P;
                        buf=buf*tmp%P;
                    }
                }
            }
            if(op==-1){
                ll invn=power(n,P-2);
                for(ll i=0;i<n;i++)
                    f[i]=f[i]*invn%P;
            }
            return;
        }
        void GetInv(ll *f,ll *g,ll m){
            if(m==1){g[0]=power(f[0],P-2);return;}
            GetInv(f,g,m>>1);GetL(m);
            for(ll i=0;i<m;i++)t1[i]=f[i],t2[i]=g[i];
            for(ll i=m;i<n;i++)t1[i]=t2[i]=0;
            NTT(t1,1);NTT(t2,1);
            for(ll i=0;i<n;i++)t1[i]=t1[i]*t2[i]%P*t2[i]%P;
            NTT(t1,-1);
            for(ll i=0;i<m;i++)g[i]=(2*g[i]-t1[i]+P)%P;
            return;
        }
        void GetSqrt(ll *f,ll *g,ll m){
            if(m==1){g[0]=1;return;}
            GetSqrt(f,g,m>>1);
            for(ll i=0;i<m;i++)t3[i]=0;
            GetInv(g,t3,m);GetL(m<<1);
            for(ll i=0;i<m;i++)t4[i]=f[i];
            for(ll i=m;i<n;i++)t3[i]=t4[i]=0;
            NTT(t3,1);NTT(t4,1);
            for(ll i=0;i<n;i++)t3[i]=t3[i]*t4[i]%P;
            NTT(t3,-1);
            for(ll i=0;i<m;i++)
                g[i]=(g[i]+t3[i])*inv2%P;
            return;
        }
    }
    signed main()
    {
        scanf("%lld%lld",&n,&m);
        for(ll i=0;i<n;i++){
            ll x;
            scanf("%lld",&x);
            if(x>m)continue;
            a[x]=P-4;
        }
        ll l=1;a[0]++;
        while(l<=m)l<<=1;
        Poly::GetSqrt(a,b,l);
        memset(a,0,sizeof(a));
        b[0]++;Poly::GetInv(b,a,l);
        for(ll i=1;i<=m;i++)
            printf("%lld
    ",a[i]*2%P);
        return 0;
    }
    
  • 相关阅读:
    OI算法复习汇总
    B. Anatoly and Cockroaches
    c# 文件过大时清空原有内容重新写入
    c# 记录内容到txt文件
    c# 关闭和重启.exe程序
    mutex 互斥量
    mysql 事件
    <asp:Button点击查询后,调用js中函数展现加载圈
    取得<asp:TextBox中的值:
    json 相关知识
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14325578.html
Copyright © 2020-2023  润新知