Codeforces 438 E 小朋友和二叉树

这篇博客探讨了快速幂运算在解决二叉树权值和问题中的应用,通过生成函数和多项式求根的方法,实现了复杂度为O(n+mlog2m)的空间和时间效率解决方案。文章详细阐述了算法原理,并给出了C语言实现代码。

题面
根据题意,设 f t f_t ft 表示权值和为 t t t 的二叉树数量,枚举根节点的权值和右子树的权值,则有:
f t = ∑ i = 1 n ∑ j = 0 t − c i f t − c i − j f j f_t=\sum_{i=1}^n \sum_{j=0}^{t-c_i} f_{t-c_i-j}f_j ft=i=1nj=0tciftcijfj
g i g_i gi 表示是否存在权值为 i i i 的点,有则 g i g_i gi 1 1 1,否则为 0 0 0。对上式进行改写:
f t = ∑ i = 1 t ∑ j = 0 t − i g i f j f t − i − j f_t=\sum_{i=1}^t \sum_{j=0}^{t-i} g_i f_j f_{t-i-j} ft=i=1tj=0tigifjftij
F ( x ) , G ( x ) F(x),G(x) F(x),G(x) 分别为 f , g f,g f,g 的生成函数。由于初始时 f ( 0 ) = 1 f(0)=1 f(0)=1,因此在模 x m + 1 x^{m+1} xm+1 意义下:
F ( x ) ≡ G ( x ) F ( x ) F ( x ) + 1 F(x) \equiv G(x)F(x)F(x) +1 F(x)G(x)F(x)F(x)+1
解一元二次方程得到:
F ( x ) = 1 ± 1 − 4 G ( x ) 2 G ( x ) F(x)=\frac{1 \pm \sqrt{1-4G(x)}}{2G(x)} F(x)=2G(x)1±14G(x)
分子有理化,得到:
F ( x ) = 2 1 ± 1 − 4 G ( x ) F(x)=\frac{2}{1 \pm \sqrt{1-4G(x)}} F(x)=1±14G(x) 2
由于 g 0 = 0 , f 0 = 1 g_0=0,f_0=1 g0=0,f0=1,确定 F ( x ) F(x) F(x) 的取值:
F ( x ) = 2 1 + 1 − 4 G ( x ) F(x)=\frac{2}{1+\sqrt{1-4G(x)}} F(x)=1+14G(x) 2
多项式开方即可。
时间复杂度 O ( n + m l o g 2 m ) O(n+mlog_2m) O(n+mlog2m),空间复杂度 O ( m ) O(m) O(m)

#include<stdio.h>
#define R register int
#define L long long
#define I inline
#define N 262144
#define P 998244353
int c[N],b[N];
I void Swap(int&x,int&y){
	int tem=x;
	x=y;
	y=tem;
}
I int Add(int x,int y){
	x+=y;
	return x>=P?x-P:x;
}
I int Div2(int x){
	return((x&1)==1?x+P:x)>>1;
}
I int PowMod(int x,int y){
	int s=1;
	while(y!=0){
		if((y&1)==1){
			s=(L)s*x%P;
		}
		x=(L)x*x%P;
		y>>=1;
	}
	return s;
}
I void NTT(int*A,int len,const short type){
	int tem=0;
	for(R i=0;i!=len;i++){
		if(i<tem){
			Swap(A[i],A[tem]);
		}
		R j=len;
		do{
			j>>=1;
			tem^=j;
		}while(tem<j);
	}
	static int w[N];
	w[0]=1;
	for(R i=1;i!=len;i<<=1){
		tem=i<<1;
		int omg=PowMod(3,P-1+(P-1)*type/tem);
		for(R j=1;j!=i;j++){
			w[j]=(L)w[j-1]*omg%P;
		}
		for(R j=0;j!=len;j+=tem){
			for(R k=j;k!=i+j;k++){
				int t1=A[k],t2=(L)A[i+k]*w[k-j]%P;
				A[k]=Add(t1,t2);
				A[i+k]=Add(t1,P-t2);
			}
		}
	}
	if(type==-1){
		tem=PowMod(len,P-2);
		for(R i=0;i!=len;i++){
			A[i]=(L)A[i]*tem%P;
		}
	}
}
I void PolyInverse(int*A,int*B,const int len){
	static int tem[N];
	B[0]=PowMod(A[0],P-2);
	for(R i=1;i!=len;i<<=1){
		int p=i<<2;
		NTT(B,p,1);
		for(R j=0;j!=i<<1;j++){
			tem[j]=A[j];
		}
		NTT(tem,p,1);
		for(R j=0;j!=p;j++){
			B[j]=(P+2-(L)tem[j]*B[j]%P)*B[j]%P;
		}
		NTT(B,p,-1);
		for(R j=i<<1;j!=p;j++){
			B[j]=tem[j]=0;
		}
	}
	for(R i=0;i!=len<<1;i++){
		tem[i]=0;
	}
}
I void PolySqrt(int*A,int*B,const int len){
	static int C[N],D[N];
	B[0]=1;
	for(R i=1;i!=len;i<<=1){
		for(R j=0;j!=i<<1;j++){
			C[j]=A[j];
			D[j]=0;
		}
		for(R j=i<<1;j!=i<<2;j++){
			D[j]=C[j]=0;
		}
		NTT(C,i<<2,1);
		PolyInverse(B,D,i<<1);
		NTT(B,i<<2,1);
		NTT(D,i<<2,1);
		for(R j=0;j!=i<<2;j++){
			B[j]=Div2(((L)C[j]*D[j]+B[j])%P);
		}
		NTT(B,i<<2,-1);
		for(R j=i<<1;j!=i<<2;j++){
			B[j]=0;
		}
	}
}
int main(){
	int n,m,x;
	scanf("%d%d",&n,&m);
	for(R i=0;i!=n;i++){
		scanf("%d",&x);
		c[x]=P-4;
	}
	c[0]=1;
	x=1;
	while(x<=m){
		x<<=1;
	}
	PolySqrt(c,b,x);
	b[0]=2;
	for(R i=0;i!=x;i++){
		c[i]=0;
	}
	PolyInverse(b,c,x);
	for(R i=1;i<=m;i++){
		x=c[i]<<1;
		printf("%d\n",x>=P?x-P:x);
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值