1. 程式人生 > >Luogu5162 WD與積木(生成函數+多項式求逆)

Luogu5162 WD與積木(生成函數+多項式求逆)

積木 math 生成 getch style fin stdin while stream

  顯然的做法是求出斯特林數,但沒有什麽優化空間。

  考慮一種暴力dp,即設f[i]為i塊積木的所有方案層數之和,g[i]為i塊積木的方案數。轉移時枚舉第一層是哪些積木,於是有f[i]=g[i]+ΣC(i,j)·f[i-j],g[i]=ΣC(i,j)·g[i-j] (j=1~i)。

  考慮優化 。我們發現這個轉移非常像卷積。寫成卷積形式,有f[i]=g[i]+Σi!·Σf[i-j]/j!/(i-j)!,g[i]=i!·Σg[i-j]/j!/(i-j)!。直接分治NTT即可。

  誒是不是強行多了個log?考慮構造出生成函數。g比較簡單,將該式寫成g[i]/i!=Σg[i-j]/j!/(i-j)!,設g[i]/i!生成函數為G(x),inv(i!)生成函數為H(x),則有G(x)=G(x)·H(x)-G(x)+1,也即G(x)=1/(2-H(x))。f同樣寫成f[i]/i!=g[i]/i!+Σf[i-j]/j!/(i-j)!,則有F(x)=G(x)+F(x)·H(x)-F(x)-1,也即F(x)=G(x)·(G(x)-1)。多項式求逆即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define P 998244353
#define N 100010
char getc(){char c=getchar();while ((c<A||c>Z)&&(c<a||c>
z)&&(c<0||c>9)) c=getchar();return c;} int gcd(int n,int m){return m==0?n:gcd(m,n%m);} int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f; } int T,n,t,r[1<<19],f[1<<19],g[1<<19],h[1<<19],tmp[1<<19]; int ksm(int a,int k) { int s=1; for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P; return s; } int inv(int a){return ksm(a,P-2);} void DFT(int *a,int n,int g) { for (int i=0;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]); for (int i=2;i<=n;i<<=1) { int wn=ksm(g,(P-1)/i); for (int j=0;j<n;j+=i) { int w=1; for (int k=j;k<j+(i>>1);k++,w=1ll*w*wn%P) { int x=a[k],y=1ll*w*a[k+(i>>1)]%P; a[k]=(x+y)%P,a[k+(i>>1)]=(x-y+P)%P; } } } } void mul(int *a,int *b,int n,int op) { for (int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1)*(n>>1); DFT(a,n,3),DFT(b,n,3); if (op==0) for (int i=0;i<n;i++) a[i]=1ll*a[i]*b[i]%P; else for (int i=0;i<n;i++) a[i]=1ll*a[i]*(P+2-1ll*a[i]*b[i]%P)%P; DFT(a,n,inv(3)); int t=inv(n); for (int i=0;i<n;i++) a[i]=1ll*a[i]*t%P; } void getinv(int n) { g[0]=1; for (int i=2;i<=n;i<<=1) { for (int j=i;j<(i<<1);j++) tmp[j]=0; for (int j=0;j<i;j++) tmp[j]=h[j]; mul(g,tmp,i<<1,1); for (int j=i;j<(i<<1);j++) g[j]=0; } } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif T=read();int t=1;while (t<=(N<<1)) t<<=1; h[0]=h[1]=1;for (int i=2;i<=N;i++) h[i]=P-1ll*(P/i)*h[P%i]%P; for (int i=2;i<=N;i++) h[i]=1ll*h[i]*h[i-1]%P; for (int i=0;i<=N;i++) h[i]=(P-h[i])%P; h[0]=(h[0]+2)%P; getinv(t); for (int i=N+1;i<t;i++) g[i]=0; memcpy(f,g,sizeof(f));f[0]--;if (f[0]<0) f[0]+=P; mul(f,g,t,0);DFT(g,t,inv(3));int u=inv(t);for (int i=0;i<t;i++) g[i]=1ll*g[i]*u%P; while (T--) { n=read(); printf("%d\n",1ll*f[n]*inv(g[n])%P); } return 0; }

Luogu5162 WD與積木(生成函數+多項式求逆)