[BZOJ3625][CF438E]小朋友和二叉树

题面:

Description

我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树。

考虑一个含有 n n 个互异正整数的序列c1,c2,,cn。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合 c1,c2,,cn c 1 , c 2 , … , c n 中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。

给出一个整数 m m ,你能对于任意的s(1sm)计算出权值为 s s 的神犇二叉树的个数吗?请参照样例以更好的理解什么样的两棵二叉树会被视为不同的。

我们只需要知道答案关于998244353( 7×17×223+1 7 × 17 × 223 + 1 ,一个质数)取模后的值。

Input

第一行有 2 2 个整数n,m(1n105,1m105)

第二行有 n n 个用空格隔开的互异的整数c1,c2,,cn(1ci105)

Output

输出 m m 行,每行有一个整数。第i行应当含有权值恰为 i i 的神犇二叉树的总数。请输出答案关于998244353( =7×17×223+1 = 7 × 17 × 223 + 1 ,一个质数)取模后的结果。

Sample Input #1

2 3
1 2

Sample Output #1

1
3
9

Sample Input #2

3 10
9 4 3

Sample Output #2

0
0
1
1
0
2
4
2
6
15

Sample Input #3

5 10
13 10 6 4 15

Sample Output #3

0
0
0
1
0
1
0
2
0
5

HINT

对于第一个样例,有9个权值恰好为3的神犇二叉树:

分析:

vi=k=0n[ck=i] v i = ∑ k = 0 n [ c k = i ]

也即 k k c中的出现次数(在此处只能为 1 1 0)。

V(x) V ( x ) v v 的生成函数:

V(x)=k=0vkxk

设权值为 i i 的神犇二叉树的个数为fi,则我们枚举根的权值和左子树的大小,可以得到一个递归式:

fi=k=0ivkj=0ikfjfikj=x+y+z=ivxfyfz f i = ∑ k = 0 i v k ∑ j = 0 i − k f j f i − k − j = ∑ x + y + z = i v x f y f z

i=0 i = 0 时,

f0=1 f 0 = 1

那么我们发现这是一个三重卷积。我们知道数列的卷积相当于生成函数的乘法,那么我们设 f f 的生成函数F(x)为:

F(x)=k=0fkxk F ( x ) = ∑ k = 0 ∞ f k x k

则我们可以得到一个关于 F(x) F ( x ) 的一元二次方程(记得要加上 f0=1 f 0 = 1 时的情况):

F(x)=V(x)F(x)2+1 F ( x ) = V ( x ) F ( x ) 2 + 1

也即:

V(x)F(x)2F(x)+1=0 V ( x ) F ( x ) 2 − F ( x ) + 1 = 0

那么我们使用二次方程求根公式得到:

F(x)=1±14V(x)2V(x) F ( x ) = 1 ± 1 − 4 V ( x ) 2 V ( x )

那么到底哪个才是真的 F(x) F ( x ) 呢?

  • F(x)=1+14V(x)2V(x) F ( x ) = 1 + 1 − 4 V ( x ) 2 V ( x )

x x 趋向于零,则F(x)就会趋向于 f0 f 0 的值。那么我们求 F(x) F ( x ) x0 x → 0 下的极限:

limx01+14V(x)2V(x) lim x → 0 1 + 1 − 4 V ( x ) 2 V ( x )

因为 v0=0 v 0 = 0 ,所以当 x0 x → 0 V(x)0 V ( x ) → 0 。则有上式相当于:

limx01+14x2x lim x → 0 1 + 1 − 4 x 2 x

显然,由于当 x0 x → 0 时有 2x0 2 x → 0 1+14x2 1 + 1 − 4 x → 2 ,则有:

limx01+14x2x= lim x → 0 1 + 1 − 4 x 2 x = ∞

舍去。

  • F(x)=114V(x)2V(x) F ( x ) = 1 − 1 − 4 V ( x ) 2 V ( x )

同理,有:

limx0114V(x)2V(x)=limx0114x2x lim x → 0 1 − 1 − 4 V ( x ) 2 V ( x ) = lim x → 0 1 − 1 − 4 x 2 x

我们发现当 x0 x → 0 114x0 1 − 1 − 4 x → 0 2x0 2 x → 0 ,则我们应用洛必达法则。分子求导可得:

d(114x)dx=d(114x)d(14x)d(14x)dx=1214x×(4)=214x d ( 1 − 1 − 4 x ) d x = d ( 1 − 1 − 4 x ) d ( 1 − 4 x ) d ( 1 − 4 x ) d x = − 1 2 1 − 4 x × ( − 4 ) = 2 1 − 4 x

分母求导可得:

d(2x)dx=2 d ( 2 x ) d x = 2

则有:

limx0114x2x=limx0214xlimx02=22=1 lim x → 0 1 − 1 − 4 x 2 x = lim x → 0 2 1 − 4 x lim x → 0 2 = 2 2 = 1

符合 f0=1 f 0 = 1

综上,有

F(x)=114V(x)2V(x) F ( x ) = 1 − 1 − 4 V ( x ) 2 V ( x )

为了方便计算,我们构造一个平方差,上下同乘 1+14V(x) 1 + 1 − 4 V ( x )

F(x)=[114V(x)][1+14V(x)]2V(x)[1+14V(x)]=21+14V(x) F ( x ) = [ 1 − 1 − 4 V ( x ) ] [ 1 + 1 − 4 V ( x ) ] 2 V ( x ) [ 1 + 1 − 4 V ( x ) ] = 2 1 + 1 − 4 V ( x )

那么我们多项式求逆+多项式求倒解决这道题。
详见https://blog.csdn.net/ez_tjy/article/details/80213166

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll p=998244353,g=3;
int nn,n,m,r[262145];
ll inv[262146],c[262145],gn[2][262145],ans;
inline ll pow(ll a,int b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
inline ll add(ll a,ll b){return a+b>p?a+b-p:a+b;}
inline ll cut(ll a,ll b){return a-b<0?a-b+p:a-b;}
void init(){
    for(n=1;n<=m;n<<=1);
    nn=n;
    gn[0][0]=gn[1][0]=1;
    gn[0][1]=pow(g,(p-1)/(n<<1));
    gn[1][1]=pow(gn[0][1],p-2);
    for(int i=2;i<(n<<1);i++){gn[0][i]=gn[0][i-1]*gn[0][1]%p;gn[1][i]=gn[1][i-1]*gn[1][1]%p;}
    inv[1]=1;
    for(int i=2;i<=(n<<1);i++)inv[i]=inv[p%i]*(p-p/i)%p;
}
void NTT(ll c[],int n,int tp=1){
    for(int i=0;i<n;i++){
        r[i]=(r[i>>1]>>1)|((i&1)*(n>>1));
        if(i<r[i])swap(c[i],c[r[i]]);
    }
    for(int i=1;i<n;i<<=1){
        for(int j=0;j<n;j+=(i<<1)){
            for(int k=0;k<i;k++){
                ll x=c[j+k],y=gn[tp!=1][nn/i*k]*c[j+k+i]%p;
                c[j+k]=add(x,y);
                c[j+k+i]=cut(x,y);
            }
        }
    }
}
void INTT(ll c[],int n){
    NTT(c,n,-1);
    for(int i=0;i<n;i++)c[i]=c[i]*inv[n]%p;
}
void inverse(ll c[],int n=n){
    static ll t[262145],tma[262145];
    t[0]=pow(c[0],p-2);
    for(int k=2;k<=n;k<<=1){
        for(int i=0;i<(k<<1);i++)tma[i]=(i<k?c[i]:0);
        for(int i=(k>>1);i<(k<<1);i++)t[i]=0;
        NTT(tma,k<<1);
        NTT(t,k<<1);
        for(int i=0;i<(k<<1);i++)t[i]=cut(add(t[i],t[i]),t[i]*t[i]%p*tma[i]%p);
        INTT(t,k<<1);
    }
    memcpy(c,t,sizeof(ll)*n);
}
void sqrt(ll c[],int n=n){
    static ll t[262145],tma[262145],tmb[262145];
    t[0]=1;
    for(int k=2;k<=n;k<<=1){
        for(int i=0;i<k;i++)tma[i]=add(t[i],t[i]);
        inverse(tma,k);
        for(int i=0;i<(k<<1);i++)tmb[i]=(i<k?c[i]:0);
        NTT(tma,k<<1);
        NTT(tmb,k<<1);
        for(int i=0;i<(k<<1);i++){
            ll tmp=tma[i];
            tma[i]=t[i];
            t[i]=tmp*tmb[i]%p;
        }
        INTT(t,k<<1);
        for(int i=0;i<(k<<1);i++)t[i]=(i<k?add(t[i],tma[i]*inv[2]%p):0);
    }
    memcpy(c,t,sizeof(ll)*n);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        c[x]=p-4;
    }
    c[0]=1;
    init();
    sqrt(c);
    c[0]=2;
    inverse(c);
    for(int i=1;i<=m;i++)printf("%lld\n",add(c[i],c[i]));
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值