1. 程式人生 > >[Codeforces438E]The Child and Binary Tree(多項式開根+多項式求逆)

[Codeforces438E]The Child and Binary Tree(多項式開根+多項式求逆)

=== ===

這裡放傳送門

=== ===

題解

這道題的意思是給定一個集合,求有多少形態不同的二叉樹滿足每個點的權值都屬於這個集合並且總點權等於i。
F(x)表示總點權等於x的的二叉樹數目,那麼由當前已經有的二叉樹構造更大的二叉樹就是先把兩個子樹拼起來然後上面再掛一個新點作為根。顯然這都可以表示成生成函式運算。那麼就是F(x)×F(x)×C(x)C(x)表示讀入的集合的生成函式。
然而這樣算出來的這一坨東西就等於F(x)了嗎?並不是,因為在計算的時候為了讓它能正確地累加只有一個根節點的那種樹,F(x)必須有一個常數項1。但是做完了上面那種運算以後那個常數項就沒有了,所以要給它加回去。也就是F

(x)=F2(x)×C(x)+1
這是一個生成函式的一元二次方程,把它解出來得到F(x)=1±14×C(x)2×C(x)
然而這好像並不是一個有多解的問題,於是觀察解出來的這兩個多項式。
誒這樣一看好像兩個並沒有什麼區別啊都挺可以的啊???
那分子分母上都有C(x)看起來不是很友好,我們把上下同時乘以114×C(x),可以發現式子變成了21±14×C(x)
這樣子看起來就比較好辦了,因為當x趨近於0的時候C(x)也趨近於0,如果下面取的是114×C(x)的話分母就會變成0,這是不可以的,那麼解就是F(x)=21+14×C(x)

然後多項式開根和多項式求逆。。。。
多項式怎麼開根????
ATP懶得寫了丟連結跑[戳這裡]

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Mod=998244353;
const int G=3;
const int inv2=499122177;
int n,m,R[300010],N,M,inv[300010],rt[300010],v[300010],c[300010],tmp[300010];
int powww(int a,int t){
    int
ans=1;a%=Mod; while (t!=0){ if (t&1) ans=((long long)ans*(long long)a)%Mod; a=((long long)a*(long long)a)%Mod;t>>=1; } return ans; } void NTT(int N,int *a,int opt){ int w,wn,x,y; for (int i=1;i<=N;i++) if (i<R[i]) swap(a[i],a[R[i]]); for (int k=1;k<N;k<<=1){ wn=powww(G,(Mod-1)/(k<<1)); for (int i=0,p=(k<<1);i<N;i+=p){ w=1; for (int j=0;j<k;j++){ x=a[i+j];y=(long long)w*(long long)a[i+j+k]%Mod; a[i+j]=(x+y)%Mod; a[i+j+k]=(x-y)%Mod; w=(long long)w*(long long)wn%Mod; } } } if (opt==-1) reverse(a+1,a+N); } void Inverse(int N,int *a,int *b){ if (N==1){b[0]=powww(a[0],Mod-2);return;} int K=(N<<1),L=0; long long inv=powww(K,Mod-2); Inverse(N>>1,a,b); for (int i=0;i<N;i++) tmp[i]=a[i]; for (int i=N;i<=K;i++) tmp[i]=0; for (int i=1;i<K;i<<=1) L++; for (int i=0;i<=K;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); NTT(K,tmp,1);NTT(K,b,1); for (int i=0;i<K;i++) tmp[i]=(long long)b[i]*(long long)(2-(long long)b[i]*(long long)tmp[i]%Mod)%Mod; NTT(K,tmp,-1); for (int i=0;i<N;i++) b[i]=(long long)tmp[i]*(long long)inv%Mod; for (int i=N;i<=K;i++) b[i]=0; } void SqRoot(int N,int *a,int *b){ if (N==1){b[0]=1;return;} int L=0,K=N<<1; int inv=powww(K,Mod-2); SqRoot(N>>1,a,b); for (int i=0;i<=N;i++) v[i]=0; Inverse(N,b,v); for (int i=0;i<N;i++) tmp[i]=a[i]; for (int i=N;i<=K;i++) tmp[i]=0; for (int i=1;i<K;i<<=1) L++; for (int i=0;i<=K;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); NTT(K,tmp,1);NTT(K,v,1);NTT(K,b,1); for (int i=0;i<K;i++) tmp[i]=(long long)inv2*(long long)(b[i]+(long long)v[i]*(long long)tmp[i]%Mod)%Mod; NTT(K,tmp,-1); for (int i=0;i<N;i++) b[i]=(long long)tmp[i]*(long long)inv%Mod; for (int i=N;i<=K;i++) b[i]=0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ int x;scanf("%d",&x); c[x]=-4; } c[0]=1; for (N=1;N<=m;N<<=1); SqRoot(N,c,rt); rt[0]++;Inverse(N,rt,inv); for (int i=1;i<=m;i++) inv[i]=(inv[i]*2%Mod+Mod)%Mod; for (int i=1;i<=m;i++) printf("%d\n",inv[i]); return 0; }