【UOJ#50】【UR #3】鏈式反應(分治FFT,動態規划)


【UOJ#50】【UR #3】鏈式反應(分治FFT,動態規划)

題面

UOJ

題解

首先把題目意思捋一捋,大概就是有\(n\)個節點的一棵樹,父親的編號大於兒子。
滿足一個點的兒子有\(2+c\)個,其中\(c\in A\),且\(c\)個兒子是葉子,另外\(2\)個存在子樹,且兩種點的鏈接的邊是不同的,求方案數。
那么就考慮一個暴力\(dp\),設\(f[i]\)表示有\(i\)個節點的樹的個數。
那么枚舉它兩個有子樹的子樹大小,然后把編號給取出來,得到:

\[f[i]=\frac{1}{2}\sum_{j}\sum_{k} {i-1\choose j}{i-1-j\choose k}f[j]f[k] \]

其中存在限制,也就是\(i-1-j-k\in A\),要乘\(\frac{1}{2}\)是因為這兩個子樹選擇的時候存在先后關系,所以同一棵子樹可能會被計算兩次。
這樣子我們就可以做\(O(n^3)\)了。
然后把式子給拆一下:

\[{i-1\choose j}{i-1-j\choose k}=\frac{(i-1)!}{j!k!(i-1-j-k)!} \]

之和\(i,j,k\)相關的可以直接和\(f[i],f[j],f[k]\)丟到一起,剩下的只和\(i/j+k\)相關。
然后預處理\(g[i]=\sum_j \frac{f[j]}{j!}\frac{f[i-j]}{(i-j)!}\)
那么

\[\frac{2f[i]}{(i-1)!}=\sum_{p=j+k}g[p]*[i-1-p\in A] \]

這樣子就可以做到\(O(n^2)\)了。
進一步發現,\(g\)\(\frac{2f}{(i-1)!}\)都是卷積的形式,所以可以直接分治\(FFT\)求解做到\(O(nlog^2)\)
然而這里有細節、、、亂搞會掛。
注意到分治\(FFT\)的過程是\([l,mid]\rightarrow [mid+1,r]\),而\(g\)數組在求解的時候是\(f\)卷上\(f\),這樣子會導致乘的數組的范圍是\([0,r-l]\),可能存在一部分值沒有被計算過。
而這個值具有對稱性,所以我們強制\(j<k\)的時候才貢獻\(2f[j]*f[k]\)
也就是如果\(i<l\)的時候,數組里丟進去的是\(2f[i]\),當\(l\le i\le mid\)的時候丟\(f[i]\),否則丟\(0\)進去。
然后兩個\(log\)就能過了。
然而這題看起來有更加優秀的做法,但是窩太菜了就不管了。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 840000
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;}
int r[MAX],W[MAX];
void NTT(int *P,int opt,int len)
{
	int N,l=0;for(N=1;N<len;N<<=1)++l;
	for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;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));W[0]=1;
		for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
		for(int j=0,p=i<<1;j<N;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
				P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%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 jc[MAX],inv[MAX],jv[MAX];
int f[MAX],g[MAX],A[MAX];
int L[MAX],R[MAX];
void CDQ(int l,int r)
{
	if(l==r)
	{
		if(l==1)f[l]=1;
		else f[l]=1ll*f[l]*inv[2]%MOD*jc[l-1]%MOD;
		return;
	}
	int mid=(l+r)>>1,N;
	CDQ(l,mid);
	for(N=1;N<=mid-l+1+r-l;N<<=1);
	for(int i=l;i<=mid;++i)L[i-l]=g[i];
	for(int i=0;i<=r-l;++i)R[i]=A[i]*jv[i];
	NTT(L,1,N);NTT(R,1,N);
	for(int i=0;i<N;++i)L[i]=1ll*L[i]*R[i]%MOD;
	NTT(L,-1,N);
	for(int i=mid+1;i<=r;++i)f[i]=(f[i]+L[i-1-l])%MOD;
	for(int i=0;i<N;++i)L[i]=R[i]=0;
	for(int i=l;i<=mid;++i)L[i-l]=1ll*f[i]*jv[i]%MOD;
	for(int i=0;i<=r-l;++i)
		if(i<l)R[i]=2ll*f[i]*jv[i]%MOD;
		else if(i<=mid)R[i]=1ll*f[i]*jv[i]%MOD;
	NTT(L,1,N);NTT(R,1,N);
	for(int i=0;i<N;++i)L[i]=1ll*L[i]*R[i]%MOD;
	NTT(L,-1,N);
	for(int i=mid+1;i<=r;++i)g[i]=(g[i]+L[i-l])%MOD;
	for(int i=0;i<N;++i)L[i]=R[i]=0;
	
	CDQ(mid+1,r);
}
int n;
char ch[MAX];
int main()
{
	scanf("%d%s",&n,ch);
	for(int i=0;i<n;++i)A[i]=ch[i]-48;
	jc[0]=jv[0]=inv[0]=inv[1]=1;
	for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	f[1]=1;
	/*
	for(int i=2;i<=n;++i)
	{
		for(int j=0;j<=i;++j)g[i]=(g[i]+1ll*f[j]*jv[j]%MOD*f[i-j]%MOD*jv[i-j]%MOD)%MOD;
		for(int j=0;j<i;++j)
			if(A[i-1-j])
				f[i]=(f[i]+1ll*g[j]*jv[i-1-j])%MOD;
		f[i]=1ll*f[i]*inv[2]%MOD*jc[i-1]%MOD;
	}
	*/
	CDQ(1,n);
	for(int i=1;i<=n;++i)printf("%d\n",f[i]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM