1. 程式人生 > >[bzoj 3625][Codeforces Round #250]小朋友和二叉樹 NTT多項式求逆+多項式開根

[bzoj 3625][Codeforces Round #250]小朋友和二叉樹 NTT多項式求逆+多項式開根

Description

我們的小朋友很喜歡電腦科學,而且尤其喜歡二叉樹。
考慮一個含有n個互異正整數的序列c[1],c[2],…,c[n]。如果一棵帶點權的有根二叉樹滿足其所有頂點的權值都在集合{c[1],c[2],…,c[n]}中,我們的小朋友就會將其稱作神犇的。並且他認為,一棵帶點權的樹的權值,是其所有頂點權值的總和。
給出一個整數m,你能對於任意的s(1<=s<=m)計算出權值為s的神犇二叉樹的個數嗎?請參照樣例以更好的理解什麼樣的兩棵二叉樹會被視為不同的。
我們只需要知道答案關於998244353(7*17*2^23+1,一個質數)取模後的值。

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(=7*17*2^23+1,一個質數)取模後的結果。

題解

據說是模版題。
考慮最裸的dp,設f[i]為權值和為i時的方案數,然後轉移為

f[i+j]=c[i]f[jk]f[j]
仔細盯著式子看可以發現是個卷積套卷積的形式,設f(x)f的生成函式,g(x)c的生成函式,可以得到f(x)=f(x)2g(x)+1
最後加的係數1是為了補齊使它是個生成函式。
有求根公式得
f(x)=1+14g(x)2g(x)
因為f
(0)=1

所以f(x)=1+14g(x)2g(x)
所以f(x)=21+14g(x)
就可以用NTT,對它求解了。
然後,寫了個在bzoj T到死的程式碼,明明在cf上怎麼交都能過。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=998244353;
const int MAXN=2000000;
ll Pinv[MAXN],P[MAXN],TMP[MAXN],QP[MAXN],PS[MAXN];
int R[MAXN],data[MAXN],n,QQQ;
ll read(){ll rt=0
;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9'){rt=rt*10+ch-'0';ch=getchar();}return rt;} void write(ll num){if(!num)putchar('0');static char str[50];static int cnt=0;while(num){str[++cnt]=num%10+'0';num/=10;} while(cnt){putchar(str[cnt--]);}putchar('\n');} ll mul(ll x,ll y){ll ret=1;while(y){if(y&1){ret=(ret*x)%MOD;}x=(x*x)%MOD;y>>=1;}return ret;} void NTT(ll *x,int len,int flag){ for(int i=0;i<len;i++)if(i<R[i])swap(x[i],x[R[i]]); for(int i=1;i<len;i<<=1){ll wn=mul(3,(MOD-1)/(i<<1)); for(int j=0;j<len;j+=(i<<1)){ll w=1; for(int k=0;k<i;k++,w=(w*wn)%MOD){ ll a=x[j+k],b=(x[j+k+i]*w)%MOD; x[j+k]=(a+b)%MOD;x[j+k+i]=(a-b+MOD)%MOD; } } } if(flag==-1){reverse(x+1,x+len);ll inv=mul(len,MOD-2);for(int i=0;i<len;i++)x[i]=(x[i]*inv)%MOD;} } void Out(ll *x,int len){ for(int i=0;i<len;i++)cout<<x[i]<<" "; cout<<endl; } void getPinv(int len,ll *x,ll *b){ if(len==1){b[0]=mul(P[0],MOD-2);return;} getPinv((len+1)>>1,x,b); int tmp=1,tmpp=len<<1,L=0; for(tmp=1;tmp<=tmpp;tmp<<=1)L++; for(int i=0;i<tmp;i++)R[i]=0; for(int i=0;i<tmp;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); for(int i=0;i<len;i++)x[i]=P[i]; for(int i=len;i<tmp;i++)x[i]=b[i]=0; NTT(x,tmp,233);NTT(b,tmp,233); for(int i=0;i<tmp;i++){ b[i]=b[i]%MOD*(2-(x[i]*b[i])%MOD)%MOD; if(b[i]<0)b[i]+=MOD; } NTT(b,tmp,-1); for(int i=len;i<tmp;i++)b[i]=0; } void getPsqrt(int len,ll *x,ll *b){ if(len==1){b[0]=1;return;} getPsqrt((len+1)>>1,x,b); int tmp=1,tmpp=len<<1,L=0; for(tmp=1;tmp<=tmpp;tmp<<=1)L++; for(int i=0;i<tmp;i++)Pinv[i]=P[i]=0; for(int i=0;i<len;i++)P[i]=(2*b[i])%MOD; getPinv(len,TMP,Pinv); for(int i=0;i<len;i++){ x[i]=QP[i]; } for(int i=len;i<tmp;i++)x[i]=0; for(int i=0;i<tmp;i++)R[i]=0; for(int i=0;i<tmp;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); NTT(x,tmp,233); NTT(Pinv,tmp,233); ll Inv = mul(2,MOD-2); for(int i=0;i<tmp;i++){ Pinv[i]=(x[i]*Pinv[i])%MOD; if(Pinv[i]<0)Pinv[i]+=MOD; } NTT(Pinv,tmp,-1); for(int i=0;i<tmp;i++)b[i]=(Pinv[i]+(Inv*b[i])%MOD)%MOD; for(int i=len;i<tmp;i++)b[i]=0; } int main(){ n=read();int m=0;QQQ=read(); for(int i=0;i<n;i++){ int q=read();m=max(q,m); data[q]++; } n=QQQ+1; for(int i=0;i<n;i++){ QP[i]=(MOD-(4*data[i])%MOD)%MOD; if(QP[i]<0)QP[i]+=MOD; } QP[0]++; getPsqrt(n,TMP,PS); for(int i=0;i<n;i++){ P[i]=(PS[i])%MOD; } P[0]++; getPinv(n,TMP,Pinv); for(int i=1;i<=QQQ;i++){ write((2*Pinv[i])%MOD); } return 0; }