1. 程式人生 > >BZOJ4944: [Noi2017]泳池(線性遞推)

BZOJ4944: [Noi2017]泳池(線性遞推)

傳送門

題解:
首先轉換問題為最終面積小等於k的個數。

注意到最終圖形的底邊會被禁止的地方分為不同段,每一段的最大面積都不能超過k,那麼我們記fi為底邊長度為i且他的上方的圖形最大面積不超過k的概率,然後就是線性遞推了。

考慮如何處理fi,這一段底邊一定會向上延伸至某個地方j被卡住,然後這個j位置相當於把這個圖形切為兩半,滿足兩邊的高度大於j位置的高度,面積小等於k,我們fi增加一維fi,j表示長度為i的底邊,高為j之後會被卡住,最大面積小等於k的概率,然後就會發現兩邊的矩形是一個子問題。 所以我們可以列舉哪個地方被卡住來DP。適當運用字首和技巧可以優化至

O(k2)(或者多項式求逆優化至O(klogk))。

注意有一個小細節是一行有多個被卡住的地方,此時要求左邊高度嚴格大於,右邊高度不嚴格大於即可。

線性遞推用特徵多項式優化,時間複雜度為O(k2logn)/O(klogklogn)

#include <bits/stdc++.h>
using namespace std;
inline int rd(int x=0) {return (scanf("%d",&x),x);}
const int K=2e3+50,mod=998244353;
inline
int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);} inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);} inline int mul(int x,int y) {return (unsigned long long)x*y%mod;} inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return
rs;} int n,q,pw[K],deg; int g[K][K],s[K],A[K],B[K],C[K],f[K]; inline void mul(int *a,int *b,int *c) { static int tp[K], tp2[K]; memcpy(tp,a,sizeof(int)*deg); memcpy(tp2,b,sizeof(int)*deg); memset(c,0,sizeof(int)*deg); for(int i=0;i<deg;i++) if(tp[i]) for(int j=0;j<deg;j++) c[i+j]=add(c[i+j],mul(tp[i],tp2[j])); const int inv=power(A[deg],mod-2); for(int i=2*deg-2;i>=deg;i--) if(c[i]) { const int t=mul(c[i],inv); for(int j=i;i-j<=deg;j--) c[j]=dec(c[j],mul(t,A[deg-(i-j)])); } } inline int getf(int b,int ans=0) { memset(C,0,sizeof(C)); memset(B,0,sizeof(B)); C[1]=1; B[0]=1; if(deg==1) C[1]=0, C[0]=mul(A[0],power(dec(0,A[1]),mod-2)); for(;b;b>>=1,mul(C,C,C)) if(b&1) mul(B,C,B); for(int i=0;i<deg;i++) ans=add(ans,mul(B[i],f[i])); return ans; } inline int solve(int lim) { memset(g,0,sizeof(g)); memset(s,0,sizeof(s)); for(int i=lim;i>=1;i--) { for(int j=1;j*i<=lim;++j) { for(int k=1;k<=j;k++) { g[j][i]=add(g[j][i],mul((k==1) ? 1 : s[k-1], (k==j) ? 1 : s[j-k])); if(k!=lim) g[j][i]=add(g[j][i],mul((k==1) ? 1 : s[k-1], g[j-k][i])); } g[j][i]=mul(g[j][i],dec(1,q)); } for(int j=1;j*i<=lim;++j) { s[j]=add(s[j],g[j][i]); s[j]=mul(s[j],pw[j]); } } memset(s,0,sizeof(s)); memset(f,0,sizeof(f)); s[0]=1; for(int j=1;j<=lim;j++) for(int i=1;i*j<=lim;++i) s[j]=add(s[j],mul(g[j][i],pw[i*j])); deg=lim+1; for(int i=deg;i>=1;i--) s[i]=mul(s[i-1],dec(1,q)); f[0]=1; for(int i=1;i<deg;i++) for(int j=1;j<=i;j++) f[i]=add(f[i],mul(s[j],f[i-j])); A[deg]=1; for(int i=1;i<=deg;i++) A[deg-i]=dec(0,s[i]); return mul(getf(n+1),power(dec(1,q),mod-2)); } int main() { n=rd(); int k=rd(); q=rd(), q=mul(q,power(rd(),mod-2)); pw[0]=1; for(int i=1;i<K;i++) pw[i]=mul(pw[i-1],q); cout<<dec(solve(k),solve(k-1)); }