首先剛好爲\(k\)很很差算,變爲至少或者至多計算而後考慮容斥。
若是是至少的話,咱們依然很難處理最大面積這個東西。因此考慮答案至多爲\(k\)的機率,再減去至多爲\(k-1\)的機率就是最終的答案。
發現要求的東西必須貼着底邊,因此對於每一列而言咱們須要考慮的就是選定區間的最低的那個不安全的格子的行號,再乘上底邊的長度。
因此考慮設\(f[n]\)表示底邊長度爲\(n\)的答案,即肯定底邊長度爲\(n\)時,面積小於等於\(k\)的答案。
那麼咱們有這樣一個轉移:
\[f[n]=\sum_{i=0}^{min(n,k)} f[n-i-1]\sum_{j=2}^{[\frac{k}{i}]+1}dp[i][j]\]
翻譯一下,首先咱們枚舉一下上一個在底邊斷開的位置,即上一次某一列的第一行就存在一個障礙,那麼就能夠知道這一次的底邊長度是\(i\),前面符合條件的是\(f[n-i-1]\),而後枚舉這一次的高度是多少,肯定高度以後乘上在這個高度上至少存在一個障礙的機率即\(dp[i][j]\)值。
注意一下咱們這裏的模型,是每次考慮一段第一行不爲障礙的東西,而分割的地方咱們強制存在障礙,即咱們考慮完了這一段以後強制在末尾放了一個障礙,因此答案是\(f[n+1]\),而末尾那個障礙是不須要放的,因此咱們實際要求的東西是\(\frac{f[n+1]}{1-p}\),其中\(p\)是否是障礙的機率。
那麼求出\(dp\)值以後,每次的係數就惟一肯定了,轉成線性常係數遞推。
考慮怎麼求解\(dp\)值。
顯然這個\(dp\)值要作的就是找到一個位置使得其障礙高度剛好爲\(j\),而後其餘位置都不小於\(j\)。
那麼咱們考慮枚舉最靠左側的那個障礙的位置\(j\),那麼它是障礙,因此機率是\(1-p\),而它下邊的都不是障礙,因此機率是\(p^{j-1}\),,那麼先枚舉這個最靠左的位置是\(l\),那麼咱們就能夠獲得轉移:
\[dp[i][j]=(1-p)p^{j-1}\sum_{l=1}^i (\sum_{k\gt j}dp[l-1][k])(\sum_{k\ge j}dp[i-l][k])\]
即考慮其左右的位置,由於這個位置是最靠左的,因此左側的最低的障礙必定都比這個位置的障礙要高,因此限制條件是\(k\gt j\),而右邊無所謂,只要不比這裏矮就好了,因此枚舉的是\(k\ge j\)。
那麼這個\(dp\)方程能夠很容易的使用後綴和優化獲得。
那麼單次轉移\(O(k)\),狀態總數\(O(klogk)\),這是由於\(i*(j-1)\le k\),因此狀態是調和級數級別的。因此這部分的\(dp\)的複雜度是\(O(k^2logk)\)。
接下來維護好後綴和,那麼\(f\)數組的求解就是一個線性常係數齊次遞推式。
暴力\(O(nk)\),矩乘\(O(k^3logn)\),這就\(90\)分了。
這個東西能夠參考這裏。
由於特徵多項式的係數是\(k\),因此多項式取模和多項式乘法能夠暴力,這部分的複雜度就是\(O(k^2)\),加上快速冪的一個\(log\),因此這部分的複雜度就是\(O(k^2logk)\)。
暴力多項式乘法不用說,暴力多項式取模就是模擬長除法的過程,顯然是一個\(O(k^2)\)的過程。
綜上,本題的複雜度就是\(O(k^2logk)\)。html
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; #define ll long long #define MOD 998244353 #define MAX 1010 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;} int f[MAX][MAX],g[MAX][MAX],pw[MAX],p,np,n,K; int M[MAX],pre[MAX<<1],A[MAX<<1],S[MAX<<1],B[MAX<<1],tmp[MAX<<1]; int Val(int a,int b){return 1ll*a*fpow(b,MOD-2)%MOD;} void Mod(int *S,int len,int K) { for(int i=len;i>=K;--i) { int t=S[i]; for(int j=0;j<=K;++j) S[i-j]=(S[i-j]+MOD-1ll*B[K-j]*t%MOD)%MOD; } } int Solve(int K) { memset(f,0,sizeof(f));memset(g,0,sizeof(g)); memset(M,0,sizeof(M));memset(pre,0,sizeof(pre)); memset(S,0,sizeof(S));memset(A,0,sizeof(A)); memset(B,0,sizeof(B));memset(tmp,0,sizeof(tmp)); for(int i=1;i<=K+2;++i)f[0][i]=g[0][i]=1; for(int i=1;i<=K;++i) for(int j=K/i+1;j;--j) { for(int l=1;l<=i;++l)f[i][j]=(f[i][j]+1ll*g[l-1][j+1]*g[i-l][j]%MOD*pw[j-1]%MOD*np)%MOD; g[i][j]=(g[i][j+1]+f[i][j])%MOD; } for(int i=0;i<=K;++i)M[i+1]=1ll*np*g[i][2]%MOD; pre[0]=1; for(int i=1;i<=K;++i) for(int j=1;j<=i;++j) pre[i]=(pre[i]+1ll*pre[i-j]*M[j])%MOD; /* for(int i=K+1;i<=n+1;++i) for(int j=1;j<=K+1;++j) pre[i]=(pre[i]+1ll*pre[i-j]*M[j])%MOD; */ if(n+1<=K)return 1ll*pre[n+1]*fpow(np,MOD-2)%MOD; K+=1; for(int i=0;i<K;++i)B[i]=(MOD-M[K-i])%MOD;B[K]=1; A[1]=1;S[0]=1;int b=n+1; while(b) { if(b&1) { for(int i=0;i<=K;++i) for(int j=0;j<=K;++j) tmp[i+j]=(tmp[i+j]+1ll*A[i]*S[j])%MOD; for(int i=0;i<=K+K;++i)S[i]=tmp[i],tmp[i]=0; Mod(S,K+K,K); } for(int i=0;i<=K;++i) for(int j=0;j<=K;++j) tmp[i+j]=(tmp[i+j]+1ll*A[i]*A[j])%MOD; for(int i=0;i<=K+K;++i)A[i]=tmp[i],tmp[i]=0; Mod(A,K+K,K); b>>=1; } int ret=0; for(int i=0;i<K;++i)ret=(ret+1ll*S[i]*pre[i])%MOD; return 1ll*ret*fpow(np,MOD-2)%MOD; } int main() { n=read();K=read();p=read();p=1ll*p*fpow(read(),MOD-2)%MOD;np=(1+MOD-p)%MOD; pw[0]=1;for(int i=1;i<=K;++i)pw[i]=1ll*pw[i-1]*p%MOD; printf("%d\n",(Solve(K)-Solve(K-1)+MOD)%MOD); return 0; }