【CF438E】The Child and Binary Tree(多项式运算,生成函数)

【CF438E】The Child and Binary Tree(多项式运算,生成函数)

题面

有一个大小为\(n\)的集合\(S\)

问所有点权都在集合中,并且点权之和分别为\([0,m]\)的二叉树的个数。

\(n,m<=10^5\)

题解

\(f(i)\)表示点权和为\(i\)的二叉树个数,\(c(i)\)是集合中数的生成函数,那么我们可以得到

\[f(n)=\sum_{i=1}^{n}c(i)\sum_{j=0}^{n-i}f(j)f(n-i-j) \]

显然有\(f(0)=1\)
构造生成函数\(F(x)=\sum_{i=0}^{\infty}f(i)x^i,C(x)=\sum_{i=0}^{\infty}c(i)x^i\)。回顾递推式,后面的显然是\(F(x)*F(x)\),而如果设\(G(x)=F(x)*F(x)\)

那么,根据上面的式子\([x^n]F(n)=\sum_{i=0}^{n}c(i)[x^n]G(n-i)\)

所以,\(F(x)=G(x)*C(x)+1=F(x)*F(x)*C(x)+1\),要\(+1\)的原因是,如果只算卷积,我们会漏算\(f(0)=1\)。直接解方程就行了。

\[F(x)=\frac{2}{1+\sqrt{1-4C(x)}} \]

负号被舍去的原因是,生成函数的\(x\)没有实际意义,因此可以带入任何数,当带入\(x=0\)时,分母为\(0\),所以底下为正号。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 500000
#define MOD 998244353
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int n,m,inv2,d[MAX];
int fpow(int a,int b)
{
	int s=1;
	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
	return s;
}
namespace NTT
{
	int r[MAX],N,M,l;
	int A[MAX],B[MAX];
	void NTT(int *P,int n,int opt)
	{
		int N=1,l=0;for(N=1;N<n;N<<=1)++l;
		for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
		for(int i=1;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
		for(int i=1;i<N;i<<=1)
		{
			int W=fpow(3,(MOD-1)/(i<<1));
			for(int p=i<<1,j=0;j<N;j+=p)
			{
				int w=1;
				for(int k=0;k<i;++k,w=1ll*w*W%MOD)
				{
					int X=P[j+k],Y=P[i+j+k]*1ll*w%MOD;
					P[j+k]=(X+Y)%MOD;P[i+j+k]=(X-Y+MOD)%MOD;
				}
			}
		}
		if(opt==-1)
		{
			reverse(&P[1],&P[N]);
			for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
		}
	}
}
int b[MAX];
int A[MAX],B[MAX],C[MAX],D[MAX],c[MAX];
void Inv(int *a,int *b,int len)
{
	if(len==1){b[0]=fpow(a[0],MOD-2);return;}
	Inv(a,b,len>>1);
	for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
	NTT::NTT(A,len<<1,1);NTT::NTT(B,len<<1,1);
	for(int i=0;i<(len<<1);++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD;
	NTT::NTT(A,len<<1,-1);
	for(int i=0;i<len;++i)b[i]=(b[i]+b[i])%MOD;
	for(int i=0;i<len;++i)b[i]=(b[i]+MOD-A[i])%MOD;
	for(int i=0;i<(len<<1);++i)A[i]=B[i]=0;
}
void Sqrt(int *a,int *b,int len)
{
	if(len==1){b[0]=a[0];return;}
	Sqrt(a,b,len>>1);
	for(int i=0;i<=len;++i)C[i]=a[i];
	Inv(b,D,len);
	NTT::NTT(C,len<<1,1);NTT::NTT(D,len<<1,1);
	for(int i=0;i<(len<<1);++i)D[i]=1ll*D[i]*C[i]%MOD;
	NTT::NTT(D,len<<1,-1);
	for(int i=0;i<len;++i)b[i]=1ll*(D[i]+b[i])%MOD*inv2%MOD;
	for(int i=0;i<=(len<<1);++i)C[i]=D[i]=0;
}
int main()
{
	n=read();m=read();inv2=fpow(2,MOD-2);
	for(int i=1;i<=n;++i)++d[read()];
	int N=1;while(N<=m)N<<=1;
	for(int i=0;i<N;++i)d[i]=(-4*d[i]+MOD)%MOD;
	++d[0];
	Sqrt(d,c,N);
	for(int i=0;i<N;++i)d[i]=0;
	c[0]=(c[0]+1)%MOD;
	Inv(c,d,N);
	for(int i=0;i<=m;++i)d[i]=(d[i]+d[i])%MOD;
	for(int i=1;i<=m;++i)printf("%d\n",d[i]);
	return 0;
	
}

posted @ 2018-04-11 20:15  小蒟蒻yyb  阅读(379)  评论(0编辑  收藏  举报