gxx_slide之The Child and Binary Tree

链接:http://codeforces.com/contest/438/problem/E
题意:给出n个不同的权值 ci(0<ci<=105) ,为不完全二叉树每个节点赋一个值,问子树和为1~m的二叉树有多少种。m,n<=100000
分析:首先,令 F[i] 为所求答案,列出递推式

F[t]=i=1nj=0tc[i]F[j]F[tc[i]j]

右侧很明显是一个卷积,但考虑到n有 105 ,我们需要把c[i]转化成生成函数求解,令 G(x)=ni=1xc[i] , F(x)=i=0F[i]xi ,那么有
F(x)=G(x)F(x)2+1
最后的 +1 是因为 F[0]=1
由于 G(x) 已知,因此这是一个二次方程,解之,得
F(x)=21±14G(x)
我们选择适当的符号,使得分母可逆即可。注意多项式开根号和求逆的写法,有点容易错。

#include<bits/stdc++.h>
using namespace std;
const int M=998244353,g=3;
typedef long long Int;
const int Maxn=262148;
Int rev2=(M+1)>>1;
Int a[Maxn],b[Maxn],c[Maxn],d[Maxn],G[Maxn],tp1[Maxn],tp2[Maxn],tp3[Maxn];
Int powmod(Int x,Int y,Int mod)
{
    Int ret=1,t=x;
    while(y){if(y&1)ret=ret*t%mod;y>>=1;t=t*t%mod;}
    return ret;
}
void rev(Int *a,int n)
{
    int i,j,k;
    for(i=1,j=n>>1;i<n-1;i++)
    {
        if(i<j)swap(a[i],a[j]);
        for(k=n>>1;j>=k;j-=k,k>>=1);j+=k;
    }
}
void dft(Int *a,int n,int flag=1)
{
    rev(a,n);
    for(int m=2;m<=n;m<<=1)
    {
        Int wm=powmod(g,(M-1)/m,M);
        if(flag<0)wm=powmod(wm,M-2,M);
        for(int k=0;k<n;k+=m)
        {
            Int w=1;
            for(int j=k;j<k+(m>>1);j++,w=w*wm%M)
            {
                Int u=a[j],v=a[j+(m>>1)]*w%M;
                a[j]=(u+v)%M;
                a[j+(m>>1)]=(u-v+M)%M;
            }
        }
    }
}
void mul(Int *a,Int *b,int n)
{
    dft(a,n);dft(b,n);
    for(int i=0;i<n;i++)a[i]=a[i]*b[i]%M;
    dft(a,n,-1);
    int revn=powmod(n,M-2,M);
    for(int i=0;i<n;i++)a[i]=a[i]*revn%M;
}
void polrev(Int *a,Int *b,Int *c,Int *d,int n)//把a变成a的%(x^n)的逆元,要求a[0]!=0
{
    for(int i=0;i<(n<<1);i++)b[i]=c[i]=d[i]=0;
    b[0]=powmod(a[0],M-2,M);
    for(int m=2;m<=n;m<<=1)
    {
        for(int j=0;j<m;j++)c[j]=d[j]=b[j];
        mul(d,c,m);
        for(int j=0;j<m;j++)c[j]=a[j];
        mul(d,c,m<<1);
        for(int j=0;j<m;j++)b[j]=(b[j]*2%M-d[j]+M)%M;
    }
    for(int i=0;i<n;i++)a[i]=b[i];
}
void polsqrt(Int *a,Int *b,Int *c,Int *d,int n)//把a变成sqrt(a)
{
    for(int i=0;i<(n<<1);i++)b[i]=c[i]=d[i]=0;
    b[0]=1;//b[0]=sqrt(a[0])
    for(int m=2;m<=n;m<<=1)
    {
        for(int j=0;j<m;j++)c[j]=b[j];
        polrev(c,tp1,tp2,tp3,m);
        for(int j=0;j<m;j++)d[j]=a[j];
        mul(d,c,m<<1);
        for(int j=0;j<m;j++)b[j]=((b[j]+d[j])%M)*rev2%M;
    }
    for(int i=0;i<n;i++)a[i]=b[i];
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int N=1;
    while(N<m+3)N<<=1;
    for(int i=0;i<n;i++){int x;scanf("%d",&x);G[x]=M-4;}G[0]=1;
    polsqrt(G,b,c,d,N);G[0]++;
    polrev(G,b,c,d,N);
    for(int i=1;i<=m;i++)printf("%I64d\n",G[i]*2%M);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值