bzoj3625 & Codeforces438E 小朋友和二叉树 生成函数+多项式开根+求逆

题目链接:传送门

我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树。
考虑一个含有n个互异正整数的序列 c [ 1 ] , c [ 2 ] , . . . , c [ n ] c[1],c[2],...,c[n] c[1],c[2],...,c[n]。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合 c [ 1 ] , c [ 2 ] , . . . , c [ n ] {c[1],c[2],...,c[n]} c[1],c[2],...,c[n]中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。
给出一个整数m,你能对于任意的s(1<=s<=m)计算出权值为s的神犇二叉树的个数吗?请参照样例以更好的理解什么样的两棵二叉树会被视为不同的。
我们只需要知道答案关于 998244353 ( 7 ∗ 17 ∗ 2 23 + 1 , 一 个 质 数 ) 998244353(7*17*2^{23}+1,一个质数) 998244353(717223+1,)取模后的值。

生成函数简介:传送门
v k v_k vk表示是否存在一个 i i i,满足 c [ i ] = k c[i]=k c[i]=k。令 v v v的生成函数 V = ∑ i = 0 m v i x i V=\sum_{i=0}^{m}v_ix^i V=i=0mvixi,其中 v i 为 0 / 1 v_i为0/1 vi0/1
f k f_k fk表示权值为 k k k的二叉树的数量,规定 f 0 = 1 f_0=1 f0=1。令 f f f的生成函数 F = ∑ i = 0 m f i x i F=\sum_{i=0}^{m}f_ix^i F=i=0mfixi

考虑用 v v v推出 f f f,不妨枚举根节点的权值,然后计算左右孩子的情况数,即:
f k = ∑ i = 0 k v i ∑ j = 0 k − i f j f k − i − j f_k=\sum_{i=0}^{k}v_i\sum_{j=0}^{k-i}f_jf_{k-i-j} fk=i=0kvij=0kifjfkij。(枚举根节点的权值 i i i,计算左右孩子的情况数,因为规定 f 0 = 1 f_0=1 f0=1,所以允许左右孩子为空)

V , F V,F V,F表示,即 F ( x ) = V ( x ) ∗ F 2 ( x ) + 1 F(x)=V(x)*F^2(x)+1 F(x)=V(x)F2(x)+1(因为 f 0 = 1 f_0=1 f0=1,所以常数项要+1)
解一元二次方程,得 F ( x ) = 1 + 1 − 4 V ( x ) 2 V ( x ) 或 1 − 1 − 4 V ( x ) 2 V ( x ) F(x)=\large\frac{1+\sqrt{1-4V(x)}}{2V(x)}或\frac{1-\sqrt{1-4V(x)}}{2V(x)} F(x)=2V(x)1+14V(x) 2V(x)114V(x)
因为 f 0 = 1 f_0=1 f0=1,所以 F ( x ) F(x) F(x)的常数项应为 1 1 1
F ( x ) = 1 + 1 − 4 V ( x ) 2 V ( x ) F(x)=\large\frac{1+\sqrt{1-4V(x)}}{2V(x)} F(x)=2V(x)1+14V(x) ,则当 x → 0 x\to{0} x0 时, F ( x ) → 1 + 1 0 = + ∞ F(x)\to\large\frac{1+1}{0}=\normalsize+\infin F(x)01+1=+,舍去( l i m lim lim打不出来……致习)
F ( x ) = 1 − 1 − 4 V ( x ) 2 V ( x ) F(x)=\large\frac{1-\sqrt{1-4V(x)}}{2V(x)} F(x)=2V(x)114V(x) ,则当 x → 0 x\to{0} x0 时, F ( x ) → 1 F(x)\to1 F(x)1,成立。

分子有理化, F ( x ) = 1 − 1 − 4 V ( x ) 2 V ( x ) = 2 1 + 1 − 4 V ( x ) F(x)=\large\frac{1-\sqrt{1-4V(x)}}{2V(x)}=\frac{2}{1+\sqrt{1-4V(x)}} F(x)=2V(x)114V(x) =1+14V(x) 2,然后搞一下多项式开根+求逆即珂。
Ps.式子中的+1是指常数项+1……差点被孙到

毒瘤代码

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define mod 998244353
#define G 3
#define inv2 499122177
#define invG 332748118
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int fpow(int b,int p) {
	int ans=1;
	while(p) {
		if(p&1)	ans=(ll)ans*b%mod;
		b=(ll)b*b%mod;
		p>>=1;
	}
	return ans;
}
inline int inv(int x) {
	return fpow(x,mod-2);
}
const int Size=4000005;
const int INF=0x3f3f3f3f;
int R[Size];
void NTT(int *a,int n,int type) {
	for(re i=0; i<n; i++) {
		if(i<R[i]) {
			swap(a[i],a[R[i]]);
		}
	}
	for(re i=1; i<n; i<<=1) {
		ll gn=fpow(type==1?G:invG,(mod-1)/(i<<1));
		for(re j=0; j<n; j+=i<<1) {
			ll g=1;
			for(re k=0; k<i; k++) {
				ll x=a[j+k],y=g*a[i+j+k]%mod;
				a[j+k]=(x+y)%mod;
				a[i+j+k]=(x-y+mod)%mod;
				g=g*gn%mod;
			}
		}
	}
	if(type==-1) {
		ll iv=inv(n);
		for(re i=0; i<n; i++) {
			a[i]=(ll)a[i]*iv%mod;
		}
	}
}
int A[Size],B[Size],ans[Size];
void Inv(int *a,int *b,int n) {		//求a的逆 
	b[0]=inv(a[0]);
	int len;
	for(len=1; len<(n<<1); len<<=1) {
		int lim=len<<1;
		for(re i=0; i<len; i++) {
			A[i]=a[i];
			B[i]=b[i];
		}
		for(re i=0; i<lim; i++) {
			R[i]=(R[i>>1]>>1)|((i&1)*len);
		}
		NTT(A,lim,1);
		NTT(B,lim,1);
		for(re i=0; i<lim; i++) {
			b[i]=(ll)B[i]*(2ll-(ll)A[i]*B[i]%mod+mod)%mod;
		}
		NTT(b,lim,-1);
		for(re i=len; i<lim; i++) {
			b[i]=0;
		}
	}
	for(re i=0; i<len; i++)	A[i]=B[i]=0;
	for(re i=n; i<len; i++)	b[i]=0;
}
void mul(int *a,int *b,int *c,int n) {
	int now=1,L=0;
	while(now<(n<<1)) {
		now<<=1;
		L++;
	}
	for(re i=0; i<now; i++) {
		R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
	}
	NTT(a,now,1);
	NTT(b,now,1);
	for(re i=0; i<now; i++) {
		c[i]=(ll)a[i]*b[i]%mod;
	}
	NTT(c,now,-1);
}
int tmpa[Size],tmpb[Size],tmpc[Size];
void Sqrt(int *a,int *b,int n) {
	b[0]=1;
	int len;
	for(len=1; len<(n<<1); len<<=1) {
		int lim=len<<1;
		for(re i=0; i<len; i++) {
			tmpa[i]=a[i];
		}
		for(re i=0; i<lim; i++) {
			R[i]=(R[i>>1]>>1)|((i&1)*len);
		}
		Inv(b,tmpb,len);
		mul(tmpa,tmpb,tmpc,lim);
		for(re i=0; i<lim; i++) {
			b[i]=(ll)(b[i]+tmpc[i])*inv2%mod;
		}
		for(re i=len; i<lim; i++) {
			b[i]=0;
		}
	}
	for(re i=0; i<len; i++)	tmpa[i]=tmpb[i]=tmpc[i]=0;
	for(re i=n; i<len; i++)	b[i]=0;
}
int n,m,a[Size],F[Size];
int main() {
	n=read();
	m=read();
	for(re i=0; i<n; i++) {
		int x=read();
		a[x]=mod-4;
	}
	a[0]=1;
	Sqrt(a,ans,m+1);
	ans[0]++;
	Inv(ans,F,m+1);
	for(re i=1; i<=m; i++) {
		printf("%d\n",(ll)F[i]*2%mod);
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值