码迷,mamicode.com
首页 > 其他好文 > 详细

【CF438E】The Child and Binary Tree(生成函数+多项式开根)

时间:2020-06-04 13:50:41      阅读:53      评论:0      收藏:0      [点我收藏+]

标签:its   https   and   bit   c++   ret   binary   logs   public   

点此看题面

大致题意: 给你\(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_i\sum_{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\n",2LL*g[i]%X);//把g修改为1/(1+sqrt(1-4g)),系数乘2即为答案
	return 0;
}

【CF438E】The Child and Binary Tree(生成函数+多项式开根)

标签:its   https   and   bit   c++   ret   binary   logs   public   

原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF438E.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!