CF438E The Child and Binary Tree 题解
CF438E The Child and Binary Tree
本文版权归 Azazel 与博客园共有,欢迎转载,但需保留此声明,并给出原文地址,谢谢合作。
原文地址:https://www.cnblogs.com/Azazel/p/15182481.html
题意
给出 n 个权值 ci,求有多少棵二叉树,其所有的点权均在这些权值之中且点权和为 S∈[1,m]。答案对 998244353 取模。
1≤n,m,ci≤105
题解
首先考虑 DP,设 fi 表示权值和为 i 且满足要求的二叉树的个数。并记 gi∈[0,1] 表示 i 是否是可行的点权之一。
先有初始值 f0=1 ,则可以推出下面这个式子:
fi=i∑j=1gii−j∑k=0fk×fi−k−j
然后不难发现把 f 和 g 看作多项式后就是个卷积形式:
f=g ∗ f2+1
由于 g 已知,我们试图通过此来推一波 f :
g∗f2−f+1=0f=1±√1−4g2g
由于一些我不会推原因,1+√1−4g2g 会被舍去,所以只有 1−√1−4g2g 合法。
理论上可做了,但是我们发现 [x0]g(x)=0 ,不能求逆,所以我们进行一波分子有理化(或者叫分母无理化?):
21+√1−4g
然后多项式开根+求逆套上来即可卷出 f 。
代码
查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const ll MOD=998244353;
ll To[5000005],N;
ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1) ret=ret*a%MOD;
b>>=1;a=a*a%MOD;
}
return ret;
}
const ll g=3,gi=qpow(g,MOD-2);
const ll Inv2=qpow(2,MOD-2);
inline ll Add(ll a,ll b){return ((a+b)%MOD+MOD)%MOD;}
inline ll Mul(ll a,ll b){return a*b%MOD;}
void NTT(ll *S,ll op)
{
for(int i=0;i<N;i++) if(i<To[i]) swap(S[i],S[To[i]]);
for(int i=1;i<N;i<<=1)
{
ll W=qpow(op==1?g:gi,(MOD-1)/(i<<1));
for(int j=0;j<N;j+=i<<1)
{
ll w=1;
for(int k=0;k<i;k++,w=w*W%MOD)
{
ll x=S[j+k],y=Mul(S[i+j+k],w);
S[j+k]=Add(x,y);S[i+j+k]=Add(x,-y);
}
}
}
if(op==-1)
{
ll Inv=qpow(N,MOD-2);
for(int i=0;i<N;i++) S[i]=Mul(S[i],Inv);
}
}
ll C[5000005];
void GetInv(int deg,ll *A,ll *B)
{
if(deg==1){B[0]=qpow(A[0],MOD-2);return;}
GetInv((deg+1)>>1,A,B);
for(N=1;N<=(deg<<1);N<<=1);
for(int i=0;i<N;i++) To[i]=(To[i>>1]>>1)|((i&1)*(N>>1)),C[i]=A[i];
for(int i=deg;i<N;i++) C[i]=0;
NTT(C,1);NTT(B,1);
for(int i=0;i<N;i++) B[i]=Mul(Add(2ll,-Mul(C[i],B[i])),B[i]);
NTT(B,-1);
for(int i=deg;i<N;i++) B[i]=0;
}
ll inv[5000005],D[5000005];
void GetSqrt(int deg,ll *A,ll *B)
{
if(deg==1){B[0]=1;return;}
GetSqrt((deg+1)>>1,A,B);
for(N=1;N<=(deg<<1);N<<=1);
for(int i=0;i<N;i++) To[i]=(To[i>>1]>>1)|((i&1)*(N>>1)),D[i]=inv[i]=0;
for(int i=0;i<deg;i++) D[i]=A[i];
GetInv(deg,B,inv);
NTT(inv,1);NTT(D,1);NTT(B,1);
for(int i=0;i<N;i++) B[i]=Mul(Add(B[i],Mul(D[i],inv[i])),Inv2);
NTT(B,-1);
for(int i=deg;i<N;i++) B[i]=0;
}
template<typename T>void read(T &x)
{
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
template<typename T>void print(T x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
ll F[1000005],G[1000005],H[1000005];
int main() {
int n,m;
scanf("%d %d",&n,&m);
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);
G[x]=1;
}
G[0]=1; for(int i=1;i<=m;i++) G[i]=(MOD-4*G[i]%MOD)%MOD;
GetSqrt(m+1,G,F);F[0]++;
GetInv(m+1,F,H);
for(int i=0;i<=m;i++) H[i]=H[i]*2%MOD;
for(int i=1;i<=m;i++) printf("%lld\n",H[i]);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步