【CF438E】小朋友和二叉樹 解題報告
【CF438E】小朋友和二叉樹
Description
我們的小朋友很喜歡電腦科學,而且尤其喜歡二叉樹。
考慮一個含有\(n\)個互異正整數的序列\(c_1,c_2,\dots,c_n\)。如果一棵帶點權的有根二叉樹滿足其所有頂點的權值都在集合\(\{c_1,c_2,\dots,c_n\}\)中,我們的小朋友就會將其稱作神犇的。並且他認為,一棵帶點權的樹的權值,是其所有頂點權值的總和。
給出一個整數\(m\),你能對於任意的\(s(1≤s≤m)\)計算出權值為\(s\)的神犇二叉樹的個數嗎?請參照樣例以更好的理解什麼樣的兩棵二叉樹會被視為不同的。
我們只需要知道答案關於\(998244353\)
Input
第一行有\(2\)個整數 \(n,m(1≤n≤10^5,1≤m≤10^5)\).
第二行有\(n\)個用空格隔開的互異的整數 \(c_1,c_2,…,c_n(1≤c[i]≤10^5)\).
Output
輸出\(m\)行,每行有一個整數。第\(i\)行應當含有權值恰為\(i\)的神犇二叉樹的總數。請輸出答案關於\(998244353\)取模後的結果。
考慮\(DP\),設\(f_i\)代表權值為\(i\)的二叉樹的個數,\(C_i\)代表是否存在權值為\(i\)的節點
\[f_n=\sum_{i=1}^nC_i\sum_{j=0}^{n-i}f_jf_{n-i-j}\]
\[f_0=1\]
然後我們發現長得很像卷積,但是沒法好好卷自己。
於是構造一波生成函式,直接表示為係數就行了
\[F=C*F*F+1\]
關於這個\(+1\),我的理解是,加了一個常數項為\(1\)的多項式表示\(f_0=1\)
然後解一下二次方程,得到
\[F=\frac{1 \pm \sqrt{1-4C}}{2C}\]
然後討論一下發現需要取+
再次化簡
\[F=\frac{2}{1+\sqrt{1-4C}}\]
套用多項式求逆+開根就可以了
Code:
#include <cstdio> #include <cctype> #include <algorithm> const int N=(1<<20)+10; const int mod=998244353,Gi=332748118,i2=499122177; int read() { int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) {x=x*10+c-'0';c=getchar();} return x; } #define add(x,y) ((x+y)%mod) #define mul(x,y) (1ll*(x)*(y)%mod) int qp(int d,int k){int f=1;while(k){if(k&1)f=mul(f,d);d=mul(d,d),k>>=1;}return f;} int C[N],sq[2][N],b[2][N],A[N],B[N],turn[N]; void NTT(int *a,int len,int typ) { for(int i=1;i<len;i++) if(i<turn[i]) std::swap(a[i],a[turn[i]]); for(int le=1;le<len;le<<=1) { int wn=qp(typ?3:Gi,(mod-1)/(le<<1)); for(int p=0;p<len;p+=le<<1) { int w=1; for(int i=p;i<p+le;i++,w=mul(w,wn)) { int tx=a[i],ty=mul(w,a[i+le]); a[i]=add(tx,ty); a[i+le]=add(tx,mod-ty); } } } if(!typ) { int inv=qp(len,mod-2); for(int i=0;i<len;i++) a[i]=mul(a[i],inv); } } void polymul(int *a,int *b,int len) { int L=-1;for(int i=1;i<len;i<<=1) ++L; for(int i=0;i<len;i++) turn[i]=turn[i>>1]>>1|(i&1)<<L,A[i]=B[i]=0; for(int i=0;i<len>>1;i++) A[i]=a[i],B[i]=b[i]; NTT(A,len,1),NTT(B,len,1); for(int i=0;i<len;i++) A[i]=mul(A[i],B[i]); NTT(A,len,0); for(int i=0;i<len;i++) a[i]=A[i]; } void polyinv(int *a,int n) { int len=2,cur=0; b[cur][0]=qp(a[0],mod-2); while(len<=(n<<2)) { cur^=1; for(int i=0;i<len>>1;i++) b[cur][i]=add(b[cur^1][i],b[cur^1][i]); polymul(b[cur^1],b[cur^1],len); polymul(b[cur^1],a,len); for(int i=0;i<len;i++) b[cur][i]=add(b[cur][i],mod-b[cur^1][i]); len<<=1; } for(int i=0;i<n;i++) a[i]=b[cur][i]; } void polysqrt(int *a,int n) { int len=2,cur=0; sq[cur][0]=1; while(len<=(n<<2)) { cur^=1; for(int i=0;i<len>>1;i++) sq[cur][i]=mul(sq[cur^1][i],i2); for(int i=0;i<len>>1;i++) sq[cur^1][i]=add(sq[cur^1][i],sq[cur^1][i]); polyinv(sq[cur^1],len); polymul(sq[cur^1],a,len); for(int i=0;i<len;i++) sq[cur][i]=add(sq[cur][i],sq[cur^1][i]); len<<=1; } for(int i=0;i<n;i++) a[i]=sq[cur][i]; } int main() { int n=read(),m=read(); for(int i=1;i<=n;i++) C[read()]=1; ++m;C[0]=1; for(int i=1;i<m;i++) C[i]=(mod-(C[i]<<2))%mod; polysqrt(C,m); C[0]=add(C[0],1); polyinv(C,m); for(int i=0;i<m;i++) C[i]=add(C[i],C[i]); for(int i=1;i<m;i++) printf("%d\n",C[i]); return 0; }
2018.12.17