CF438EThe Child and Binary Tree(生成函数+多项式开根)
Posted chenxiaoran666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF438EThe Child and Binary Tree(生成函数+多项式开根)相关的知识,希望对你有一定的参考价值。
大致题意: 给你(n)个数,对于(i∈[1,m]),求出有多少棵不同的二叉树,满足权值都取自这(n)个数中,且权值和恰为(i)。
推式子
我们令(f_i)表示权值和恰为(i)的二叉树个数,(g_i)表示(i)是否为给出的数((0/1))。
对于(f_0),易知(f_0=1)。
对于(f_n(n>0)),一个显然的转移,我们枚举(i)表示根节点填的数,然后枚举(j)表示左子树权值和,就有:
[f_n=sum_{i=0}^ng_isum_{j=0}^{n-i}f_{j}f_{V-i-j}
]
然后发现这其实是一个卷积的形式。
令(F,G)分别为(f,g)的生成函数,得到:(注意最后的加(1)是因为(f_0=1))
[F=GF^2+1
]
接下来,最神奇的一步来了。根据一元二次方程的求根公式,得到:
[F=frac{1±sqrt{1-4G}}{2G}
]
考虑当(F=frac{1+sqrt{1-4G}}{2G})时,(F)是不收敛的,故舍去。
而当(F=frac{1-sqrt{1-4G}}{2G})时,(F)就能够满足要求了。
此时还可以进一步转化让式子更简单:分母无理化分子有理化。得到:
[F=frac2{1+sqrt{1-4G}}
]
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define V 100000
#define X 998244353
using namespace std;
int n,m,g[V<<2],s[V<<2];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class Poly//多项式
{
private:
int P,L,R[V<<2],t[V<<2],k[V<<2],PR,IPR,I2;
I void T(int *s,CI p)//NTT
{
RI i,j,k,U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x);
for(i=1;i^P;i<<=1) for(U=QP(p,(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,k=0;k^i;
S=1LL*S*U%X,++k) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
}
public:
I Poly() {PR=3,IPR=QP(3,X-2),I2=X+1>>1;}
I void Inv(CI n,int *a,int *b)//多项式求逆
{
if(!n) return (void)(b[0]=QP(a[0],X-2));Inv(n>>1,a,b);
RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
for(i=0;i<=n;++i) t[i]=a[i];W(i^P) t[i++]=0;
for(T(b,PR),T(t,PR),i=0;i^P;++i) b[i]=(2LL*b[i]-1LL*t[i]*b[i]%X*b[i]%X+X)%X;
RI t=QP(P,X-2);for(T(b,IPR),i=0;i<=n;++i) b[i]=1LL*b[i]*t%X;W(i^P) b[i++]=0;
}
I void Sqrt(CI n,int *a,int *b)//多项式开根
{
if(!n) return (void)(b[0]=1);Sqrt(n>>1,a,b);
RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
for(i=0;i^P;++i) k[i]=0;for(Inv(n,b,k),i=0;i<=n;++i) t[i]=a[i];W(i^P) t[i++]=0;
for(T(t,PR),T(b,PR),T(k,PR),i=0;i^P;++i) b[i]=(1LL*b[i]*b[i]+t[i])%X*k[i]%X*I2%X;
RI t=QP(P,X-2);for(T(b,IPR),i=0;i<=n;++i) b[i]=1LL*b[i]*t%X;W(i^P) b[i++]=0;
}
}P;
int main()
{
RI i,x;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&x),g[x]=1;
for(g[0]=i=1;i<=V;++i) g[i]=X-4LL*g[i]%X;P.Sqrt(V,g,s),++s[0]==X&&(s[0]=0);//把g修改为1-4g,令s=1+sqrt(1-4g)
for(memset(g,0,sizeof(g)),P.Inv(V,s,g),i=1;i<=m;++i) printf("%d
",2LL*g[i]%X);//把g修改为1/(1+sqrt(1-4g)),系数乘2即为答案
return 0;
}
以上是关于CF438EThe Child and Binary Tree(生成函数+多项式开根)的主要内容,如果未能解决你的问题,请参考以下文章
CF(438D) The Child and Sequence(线段树)
CF438D The Child and Sequence 线段树