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]}中,我們的小朋友就會將其稱作神犇的。並且他認爲,一棵帶點權的樹的權值,是其所有頂點權值的總和。
給出一個整數m,你能對於任意的s(1<=s<=m)計算出權值爲s的神犇二叉樹的個數嗎?請參照樣例以更好的理解什麼樣的兩棵二叉樹會被視爲不同的。
我們只需要知道答案關於998244353(717223+1,)998244353(7*17*2^{23}+1,一個質數)取模後的值。

生成函數簡介:傳送門
vkv_k表示是否存在一個ii,滿足c[i]=kc[i]=k。令vv的生成函數V=i=0mvixiV=\sum_{i=0}^{m}v_ix^i,其中vi0/1v_i爲0/1
fkf_k表示權值爲kk的二叉樹的數量,規定f0=1f_0=1。令ff的生成函數F=i=0mfixiF=\sum_{i=0}^{m}f_ix^i

考慮用vv推出ff,不妨枚舉根節點的權值,然後計算左右孩子的情況數,即:
fk=i=0kvij=0kifjfkijf_k=\sum_{i=0}^{k}v_i\sum_{j=0}^{k-i}f_jf_{k-i-j}。(枚舉根節點的權值ii,計算左右孩子的情況數,因爲規定f0=1f_0=1,所以允許左右孩子爲空)

V,FV,F表示,即F(x)=V(x)F2(x)+1F(x)=V(x)*F^2(x)+1(因爲f0=1f_0=1,所以常數項要+1)
解一元二次方程,得F(x)=1+14V(x)2V(x)114V(x)2V(x)F(x)=\large\frac{1+\sqrt{1-4V(x)}}{2V(x)}或\frac{1-\sqrt{1-4V(x)}}{2V(x)}
因爲f0=1f_0=1,所以F(x)F(x)的常數項應爲11
F(x)=1+14V(x)2V(x)F(x)=\large\frac{1+\sqrt{1-4V(x)}}{2V(x)},則當x0x\to{0} 時,F(x)1+10=+F(x)\to\large\frac{1+1}{0}=\normalsize+\infin,捨去(limlim打不出來……致習)
F(x)=114V(x)2V(x)F(x)=\large\frac{1-\sqrt{1-4V(x)}}{2V(x)},則當x0x\to{0} 時,F(x)1F(x)\to1,成立。

分子有理化,F(x)=114V(x)2V(x)=21+14V(x)F(x)=\large\frac{1-\sqrt{1-4V(x)}}{2V(x)}=\frac{2}{1+\sqrt{1-4V(x)}},然後搞一下多項式開根+求逆即珂。
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章