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

正题

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


题目大意

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


解题思路

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

\[f_n=\sum_{w=1}^ng_w\sum_{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{1\pm \sqrt{1-4G}}{2G} \]

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

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

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

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

时间复杂度\(O(n\log 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\n",a[i]*2%P);
    return 0;
}
posted @ 2021-01-25 15:45  QuantAsk  阅读(48)  评论(0编辑  收藏  举报