bzoj3625:小朋友和二叉树(生成函数+多项式求逆+多项式开方)

这是一道关于二叉树和生成函数的数学难题。题目要求求解所有权值和为特定值的二叉树数量。通过构建生成函数g(x),定义f(x)表示答案的生成函数,并利用递推关系f(x)=g(x)f2(x)+1来解决问题。最后,需要对多项式进行开方和逆运算。虽然在codeforces上成功解决,但在bzoj上因常数过大未通过。
摘要由CSDN通过智能技术生成

传送门
c o d e f o r c e s 传 送 门 codeforces传送门 codeforces
生成函数好题。
卡场差评至今未过
题意简述: n n n个点的二叉树,每个点的权值KaTeX parse error: Expected 'EOF', got '\inC' at position 4: v_i\̲i̲n̲C̲=\{a_1,a_2,...a…,定义一棵树的权值为所有点的权值之和,问有多少棵树满足其权值等于 i ( i = 1 , 2 , . . . , m ) i(i=1,2,...,m) i(i=1,2,...,m)


对每个点的值构造生成函数 g ( x ) = ∑ n a n x n ( a n = [ n ∈ C ] ) g(x)=\sum_na_nx^n(a_n=[n\in C]) g(x)=nanxn(an=[nC]),令 f ( x ) f(x) f(x)表示答案的生成函数。
那么 f ( x ) = g ( x ) f 2 ( x ) + 1 f(x)=g(x)f^2(x)+1 f(x)=g(x)f2(x)+1 注意空树的情况,这个递推式相当于考虑自己的权值以及左右子树的权值
然后解方程: f ( x ) = 2 1 − 1 − 4 g ( x ) f(x)=\frac 2{1-\sqrt{1-4g(x)}} f(x)=114g(x) 2
然后上多项式开方和多项式求逆即可。

悲伤的故事:封装了一波多项式运算导致常数太大,于是只能在 c o d e f o r c e s codeforces codeforces上水过, b z o j bzoj bzoj至今未过
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int mod=998244353;
int n,lim,tim,m;
vector<int>A,B,pos,Inv;
#define add(a,b) ((a)+(b)>=mod?(a)+(b)-mod:(a)+(b))
#define dec(a,b) ((a)>=(b)?(a)-(b):(a)-(b)+mod)
#define mul(a,b) ((ll)(a)*(b)%mod)
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline void ntt(vector<int>&a,const int&type){
	for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
	for(ri mid=1,wn,mult=(mod-1)/2,typ=type==1?3:(mod+1)/3;mid<lim;mid<<=1,mult>>=1){
		wn=ksm(typ,mult);
		for(ri j=0,len=mid<<1;j<lim;j+=len)for(ri w=1,a0,a1,k=0;k<mid;++k,w=mul(w,wn)){
			a0=a[j+k],a1=mul(w,a[j+k+mid]);
			a[j+k]=add(a0,a1),a[j+k+mid]=dec(a0,a1);
		}
	}
	if(type==-1)for(ri i=0,inv=ksm(lim,mod-2);i<lim;++i)a[i]=mul(a[i],inv);
}
inline void init(const int&up){
	lim=1,tim=0;
	while(lim<=up)lim<<=1,++tim;
	pos.resize(lim),pos[0]=0;
	for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
struct poly{
	vector<int>a;
	inline int deg()const{return a.size()-1;}
	poly(int k,int x=0){a.resize(k+1),a[k]=x;}
	inline int&operator[](const int&k){return a[k];}
	inline const int&operator[](const int&k)const{return a[k];}
	inline poly extend(const int&k){poly ret=*this;return ret.a.resize(k),ret;}
	friend inline poly operator+(const poly&a,const poly&b){
		poly ret(max(a.deg(),b.deg()));
		for(ri i=0;i<=a.deg();++i)ret[i]=add(ret[i],a[i]);
		for(ri i=0;i<=b.deg();++i)ret[i]=add(ret[i],b[i]);
		return ret;
	}
	friend inline poly operator-(const poly&a,const poly&b){
		poly ret(max(a.deg(),b.deg()));
		for(ri i=0;i<=a.deg();++i)ret[i]=add(ret[i],a[i]);
		for(ri i=0;i<=b.deg();++i)ret[i]=dec(ret[i],b[i]);
		return ret;
	}
	friend inline poly operator*(const int&a,const poly&b){
		poly ret(b.deg());
		for(ri i=0;i<=b.deg();++i)ret[i]=mul(a,b[i]);
		return ret;
	}
	friend inline poly operator*(const poly&a,const poly&b){
		int n=a.deg(),m=b.deg();
		init(n+m),A.resize(lim),B.resize(lim);
		poly ret(lim-1);
		for(ri i=0;i<=n;++i)A[i]=a[i];
		for(ri i=0;i<=m;++i)B[i]=b[i];
		for(ri i=n+1;i<lim;++i)A[i]=0;
		for(ri i=m+1;i<lim;++i)B[i]=0;
		ntt(A,1),ntt(B,1);
		for(ri i=0;i<lim;++i)A[i]=mul(A[i],B[i]);
		return ntt(A,-1),ret.a=A,ret;
	}
	inline poly poly_inv(poly a,const int&k){
		a=a.extend(k);
		if(k==1)return poly(0,ksm(a[0],mod-2));
		poly f0=poly_inv(a,(k+1)>>1);
		return (2*f0-((f0*f0.extend(k))*a).extend(k)).extend(k);
	}
	inline poly poly_sqrt(poly a,const int&k){
		a=a.extend(k);
		if(k==1)return poly(0,1);
		poly f0=poly_sqrt(a,(k+1)>>1).extend(k);
		return (((f0*f0).extend(k)+a)*poly_inv((2*f0),k)).extend(k);
	}
};
int main(){
	n=read(),m=read();
	int len;
	for(len=1;len<=m;len<<=1);
	poly sqr=(len);
	for(ri i=1,v;i<=n;++i){
		v=read();
		if(v<=m)sqr[v]=mod-4;
	}
	++sqr[0],sqr=sqr.poly_sqrt(sqr,len),++sqr[0],sqr=sqr.poly_inv(sqr,len);
	for(ri i=1;i<=m;++i)cout<<mul(sqr[i],2)<<'\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值