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(生成函数+多项式开根)的主要内容,如果未能解决你的问题,请参考以下文章

CF438D The Child and Sequence

CF(438D) The Child and Sequence(线段树)

CF438D The Child and Sequence 线段树

CF438D The Child and Sequence

CF438D The Child and Sequence(线段树)

CF438D The Child and Sequence