题目大意: 有一个 n + 2 n+2 n+2 行 m m m 列的矩阵,上下两行不能被侵蚀,下面经过 k k k 天,每天每一行最左边和最右边的一个格子可能会被侵蚀,问最后最上面一行和最下面一行连通的概率是多少?
题解
相当经典的前缀和优化dp。
假如 k k k 天后,一行的前面被侵蚀了 i i i 个,这种情况的概率就是 A i = ( k i ) p i ( 1 − p ) k − i A_i=\binom k i p^i(1-p)^{k-i} Ai=(ik)pi(1−p)k−i,可以预处理出 A A A。
然后先考虑一个暴力dp: f i , l , r f_{i,l,r} fi,l,r 表示从上到下第 i i i 行, k k k 天后剩下的区间为 [ l , r ] [l,r] [l,r],并且这个剩下部分与最上面一行连通的概率,转移为 f i , l , r = A ( l − 1 ) A ( m − r ) ∑ [ l ′ , r ′ ] ∩ [ l , r ] ≠ ϕ f i − 1 , l ′ , r ′ f_{i,l,r}=A(l-1)A(m-r)\sum_{[l',r']\cap [l,r]\neq \phi} f_{i-1,l',r'} fi,l,r=A(l−1)A(m−r)∑[l′,r′]∩[l,r]=ϕfi−1,l′,r′。
考虑优化这个dp,注意到下面的限制本质是
l
′
≤
r
&
&
r
′
≥
l
l'\leq r~\&\&~r'\geq l
l′≤r && r′≥l,那么就可以将转移表示成另外一种形式:
f
i
,
l
,
r
=
A
l
−
1
A
m
−
r
(
(
∑
1
≤
l
′
≤
r
′
≤
m
f
i
−
1
,
l
′
,
r
′
)
−
(
∑
1
≤
l
′
≤
r
′
<
l
f
i
−
1
,
l
′
,
r
′
)
−
(
∑
r
<
l
′
≤
r
′
≤
m
f
i
−
1
,
l
′
,
r
′
)
)
f_{i,l,r}=A_{l-1}A_{m-r}\left(\left(\sum_{1\leq l'\leq r'\leq m} f_{i-1,l',r'}\right)-\left(\sum_{1\leq l'\leq r'<l} f_{i-1,l',r'}\right)-\left(\sum_{r<l'\leq r'\leq m} f_{i-1,l',r'}\right)\right)
fi,l,r=Al−1Am−r((1≤l′≤r′≤m∑fi−1,l′,r′)−(1≤l′≤r′<l∑fi−1,l′,r′)−(r<l′≤r′≤m∑fi−1,l′,r′))
不妨记
p
r
e
i
,
p
=
∑
1
≤
l
≤
r
≤
p
f
i
,
l
,
r
,
s
u
f
i
,
p
=
∑
p
≤
l
≤
r
≤
m
f
i
,
l
,
r
pre_{i,p}=\sum_{1\leq l\leq r\leq p} f_{i,l,r}~,~suf_{i,p}=\sum_{p\leq l\leq r\leq m}f_{i,l,r}
prei,p=∑1≤l≤r≤pfi,l,r , sufi,p=∑p≤l≤r≤mfi,l,r,代入变成了很简洁的东西:
f
i
,
l
,
r
=
A
l
−
1
A
m
−
r
(
p
r
e
i
−
1
,
m
−
p
r
e
i
−
1
,
l
−
1
−
s
u
f
i
−
1
,
r
+
1
)
f_{i,l,r}=A_{l-1}A_{m-r}(pre_{i-1,m}-pre_{i-1,l-1}-suf_{i-1,r+1})
fi,l,r=Al−1Am−r(prei−1,m−prei−1,l−1−sufi−1,r+1)
考虑跳过
f
f
f,直接尝试进行递推
p
r
e
pre
pre,展开
p
r
e
pre
pre 得到:
p
r
e
i
,
p
=
∑
l
=
1
p
∑
r
=
l
p
A
l
−
1
A
m
−
r
(
p
r
e
i
−
1
,
m
−
p
r
e
i
−
1
,
l
−
1
−
s
u
f
i
−
1
,
r
+
1
)
pre_{i,p}=\sum_{l=1}^p\sum_{r=l}^p A_{l-1}A_{m-r}(pre_{i-1,m}-pre_{i-1,l-1}-suf_{i-1,r+1})
prei,p=l=1∑pr=l∑pAl−1Am−r(prei−1,m−prei−1,l−1−sufi−1,r+1)
不难想到
p
r
e
i
,
p
pre_{i,p}
prei,p 是可以从
p
r
e
i
,
p
−
1
pre_{i,p-1}
prei,p−1 递推过来的,即:
p
r
e
i
,
p
=
p
r
e
i
,
p
−
1
+
∑
l
=
1
p
A
l
−
1
A
m
−
p
(
p
r
e
i
−
1
,
m
−
p
r
e
i
−
1
,
l
−
1
−
s
u
f
i
−
1
,
p
+
1
)
=
p
r
e
i
,
p
−
1
+
(
A
m
−
p
p
r
e
i
−
1
,
m
∑
l
=
1
p
A
l
−
1
)
−
(
A
m
−
p
∑
l
=
1
p
A
l
−
1
p
r
e
i
−
1
,
p
−
1
)
−
(
A
m
−
p
s
u
f
i
−
1
,
p
+
1
∑
l
=
1
p
A
l
−
1
)
\begin{aligned} pre_{i,p}&=pre_{i,p-1}+\sum_{l=1}^p A_{l-1}A_{m-p}(pre_{i-1,m}-pre_{i-1,l-1}-suf_{i-1,p+1})\\ &=pre_{i,p-1}+\left(A_{m-p}pre_{i-1,m}\sum_{l=1}^p A_{l-1}\right)-\left(A_{m-p}\sum_{l=1}^p A_{l-1}pre_{i-1,p-1}\right)-\left(A_{m-p}suf_{i-1,p+1}\sum_{l=1}^p A_{l-1}\right) \end{aligned}
prei,p=prei,p−1+l=1∑pAl−1Am−p(prei−1,m−prei−1,l−1−sufi−1,p+1)=prei,p−1+(Am−pprei−1,ml=1∑pAl−1)−(Am−pl=1∑pAl−1prei−1,p−1)−(Am−psufi−1,p+1l=1∑pAl−1)
不难发现三个 ∑ \sum ∑ 都是可以使用前缀和优化的,于是就做完了。
s u f suf suf 的递推是类似的,可以自己手玩一下,或者直接看代码。
边界为 p r e 0 , m = s u f 0 , 1 = 1 pre_{0,m}=suf_{0,1}=1 pre0,m=suf0,1=1,答案为 p r e n , m pre_{n,m} pren,m,时间复杂度 O ( n m + k ) O(nm+k) O(nm+k)。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mod 1000000007
int n,m,pa,pb,p,k;
int fac[100010],inv_fac[100010],d1[100010],d2[100010],A[2010];
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
void FacInit(){
fac[0]=inv_fac[0]=1;
for(int i=1;i<=k;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv_fac[k]=ksm(fac[k],mod-2);
for(int i=k-1;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
}
int Binom(int x,int y){
if(x<y||y<0)return 0;
return 1ll*fac[x]*inv_fac[y]%mod*inv_fac[x-y]%mod;
}
void Init(){
p=1ll*pa*ksm(pb,mod-2)%mod;d1[0]=1;
for(int i=1;i<=k;i++)d1[i]=1ll*d1[i-1]*p%mod;
p=(1-p+mod)%mod;d2[0]=1;
for(int i=1;i<=k;i++)d2[i]=1ll*d2[i-1]*p%mod;
FacInit();for(int i=0;i<=m&&i<=k;i++)
A[i]=1ll*Binom(k,i)*d1[i]%mod*d2[k-i]%mod;
}
int pre[2][2010],suf[2][2010],cur=0;
int sumA[2010],Ap[2010],As[2010];
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
int add(int x){return x>=mod?x-mod:x;}
void dp(){
sumA[0]=A[0];for(int i=1;i<=m;i++)
sumA[i]=add(sumA[i-1]+A[i]);
pre[cur][m]=suf[cur][1]=1;
while(n--){
cur^=1;
/*memset(pre[cur],0,sizeof(pre[cur]));
memset(suf[cur],0,sizeof(suf[cur]));
for(int i=1;i<=m;i++)
for(int l=1;l<=i;l++)
for(int r=l;r<=i;r++)
add(pre[cur][i],1ll*A[l-1]*A[m-r]%mod*(pre[cur^1][m]-add(pre[cur^1][l-1]+suf[cur^1][r+1])+mod)%mod);
for(int i=1;i<=m;i++)
for(int l=i;l<=m;l++)
for(int r=l;r<=m;r++)
add(suf[cur][i],1ll*A[l-1]*A[m-r]%mod*(pre[cur^1][m]-add(pre[cur^1][l-1]+suf[cur^1][r+1])+mod)%mod);*/
for(int i=1;i<=m;i++){
pre[cur][i]=(1ll*pre[cur^1][m]*A[m-i]%mod*sumA[i-1]%mod
-1ll*A[m-i]*Ap[i]%mod+mod
-1ll*A[m-i]*suf[cur^1][i+1]%mod*sumA[i-1]%mod+mod)%mod;
suf[cur][i]=(1ll*A[i-1]*pre[cur^1][m]%mod*sumA[m-i]%mod
-1ll*A[i-1]*pre[cur^1][i-1]%mod*sumA[m-i]%mod+mod
-1ll*A[i-1]*As[i]%mod+mod)%mod;
}
pre[cur][0]=suf[cur][m+1]=0;
for(int i=1;i<=m;i++)
add(pre[cur][i],pre[cur][i-1]);
for(int i=m;i>=1;i--)
add(suf[cur][i],suf[cur][i+1]);
Ap[0]=0;As[m+1]=0;
for(int i=1;i<=m;i++)
Ap[i]=add(Ap[i-1]+1ll*A[i-1]*pre[cur][i-1]%mod);
for(int i=m;i>=1;i--)
As[i]=add(As[i+1]+1ll*A[m-i]*suf[cur][i+1]%mod);
}
}
int main()
{
scanf("%d %d %d %d %d",&n,&m,&pa,&pb,&k);
Init();dp();printf("%d",pre[cur][m]);
}