洛谷P5162 WD與積木 [DP,NTT]
傳送門
題解
真是非常套路的一道題……
考慮\(DP\):設\(f_n\)為\(n\)個積木能搭出的方案數,\(g_n\)為所有方案的高度之和。
容易得到轉移方程:
\[ \begin{align*} &f_n=[n=0]+\sum_{i=1}^n {n \choose i} f_{n-i}\&g_n=\sum_{i=1}^n {n \choose i} (f_{n-i}+g_{n-i}) \end{align*} \]
發現\(f_n\)似乎更容易搞出來,我們先搞\(f_n\)。
由轉移方程可以得到:
\[ \frac{f_n}{n!}=[n=0]+\sum_{i=1}^n \frac{1}{i!} \frac{f_{n-i}}{(n-i)!} \]
設
\[ F(x)=\sum_n \frac{f_n}{n!} x^n\S(x)=\sum_{n=1}^{\infty} \frac{1}{n!} x^n \]
則有
\[ \begin{align*} F(x)-1&=F(x)S(x)\F(x)&=\frac{1}{1-S(x)} \end{align*} \]
多項式求逆即可。
接下來是求\(g_n\)。
令\(t_n=f_n+g_n\),則有
\[ \frac{g_n}{n!}=\sum_{i=1}^n \frac{1}{i!} \frac{t_{n-i}}{(n-i)!} \]
設
\[ \begin{align*} &G(x)=\sum_n \frac{g_n}{n!} x^n\&T(x)=\sum_n \frac{t_n}{n!}=F(x)+G(x) \end{align*} \]
可以得到
\[ G(x)=S(x)T(x)=S(x)F(x)+S(x)G(x)\G(x)=\frac{S(x)F(x)}{1-S(x)}=S(x)[F(x)]^2=F(x)(F(x)-1) \]
NTT即可。
最後\(ans_n=\frac{g_n}{f_n}\)。
代碼
#include<bits/stdc++.h> namespace my_std{ using namespace std; #define pii pair<int,int> #define fir first #define sec second #define MP make_pair #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define drep(i,x,y) for (int i=(x);i>=(y);i--) #define go(x) for (int i=head[x];i;i=edge[i].nxt) #define sz 400404 typedef long long ll; const ll mod=998244353; template<typename T> inline void read(T& t) { t=0;char f=0,ch=getchar(); double d=0.1; while(ch>‘9‘||ch<‘0‘) f|=(ch==‘-‘),ch=getchar(); while(ch<=‘9‘&&ch>=‘0‘) t=t*10+ch-48,ch=getchar(); if(ch==‘.‘) { ch=getchar(); while(ch<=‘9‘&&ch>=‘0‘) t+=d*(ch^48),d*=0.1,ch=getchar(); } t=(f?-t:t); } template<typename T,typename... Args> inline void read(T& t,Args&... args){read(t); read(args...);} void file() { #ifndef ONLINE_JUDGE freopen("a.txt","r",stdin); #endif } // inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;} } using namespace my_std; inline ll ksm(ll x,int y) { ll ret=1; for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod; return ret; } ll inv(ll x){return ksm(x,mod-2);} int r[sz],limit; void NTT_init(int n) { limit=1;int l=-1; while (limit<=n+n) limit<<=1,++l; rep(i,0,limit-1) r[i]=(r[i>>1]>>1)|((i&1)<<l); } void NTT(ll *a,int type) { rep(i,0,limit-1) if (i<r[i]) swap(a[i],a[r[i]]); for (int mid=1;mid<limit;mid<<=1) { ll Wn=ksm(3,(mod-1)/mid>>1);if (type==-1) Wn=inv(Wn); for (int len=mid<<1,j=0;j<limit;j+=len) { ll w=1; for (int k=0;k<mid;k++,w=w*Wn%mod) { ll x=a[j+k],y=w*a[j+k+mid]%mod; a[j+k]=(x+y)%mod;a[j+k+mid]=(mod+x-y)%mod; } } } if (type==1) return; ll I=inv(limit); rep(i,0,limit-1) a[i]=a[i]*I%mod; } ll tmp1[sz],tmp2[sz]; void PolyInv(ll *a,ll *f,int n) { if (n==1) return (void)(f[0]=inv(a[0])); int mid=(n+1)>>1; PolyInv(a,f,mid); NTT_init(n); rep(i,0,mid-1) tmp1[i]=f[i]; rep(i,0,n-1) tmp2[i]=a[i]; NTT(tmp1,1);NTT(tmp2,1); rep(i,0,limit-1) tmp1[i]=tmp1[i]*(mod+2-tmp1[i]*tmp2[i]%mod)%mod; NTT(tmp1,-1); rep(i,0,n-1) f[i]=tmp1[i]; rep(i,0,limit-1) tmp1[i]=tmp2[i]=0; } ll fac[sz],_fac[sz]; void init(){fac[0]=_fac[0]=1;rep(i,1,sz-1) _fac[i]=inv(fac[i]=fac[i-1]*i%mod);} ll f[sz],g[sz],s[sz]; ll t1[sz],t2[sz],t3[sz],t4[sz]; int main() { file(); init(); int n=1e5+5,T; rep(i,1,n) s[i]=mod-_fac[i]; ++s[0]; PolyInv(s,t1,n); rep(i,1,n) f[i]=t1[i];f[0]=1; rep(i,0,n) t2[i]=t3[i]=f[i];--t3[0]; NTT_init(n); NTT(t2,1);NTT(t3,1); rep(i,0,limit-1) t4[i]=t2[i]*t3[i]%mod; NTT(t4,-1); rep(i,1,n) g[i]=t4[i]; read(T); while (T--) read(n),printf("%lld\n",g[n]*inv(f[n])%mod); }
洛谷P5162 WD與積木 [DP,NTT]