- 初始有一张((n+2) imes m)的网格图。
- 共操作(k)轮,每轮除第一行和最后一行外每行的第一个格子和最后一个格子均有(p=frac ab)的概率被删除。
- 求最终图连通的概率。
- (n,mle1500,kle10^5)
暴力动态规划
首先(k)相对(n,m)要大许多,肯定不会把它放到(DP)状态中。
因此我们只需要考虑最终状态,那就只需要设(q_i)表示在(k)轮删除操作中恰好进行(i)次的概率:
[q_i=C_k^ip^i(1-p)^{k-i}
]
由于前后的删除相对独立,因此设(w_{l,r})表示最后被删得恰好只剩([l,r])的概率,就有:
[w_{l,r}=p_{l-1} imes p_{m-r}
]
这样一来,如果我们设(f_{i,l,r})表示第(i)行最终被删得只剩([l,r])且前(i)行连通的概率,就只需考虑这一行与上一行是否连通:
[f_{i,l,r}=w_{l,r} imessum_{[l,r]cap[l',r']
ot=emptyset}f_{i-1,l',r'}
]
两个区间有交,可以容斥用总方案数减去无交的方案数:
[f_{i,l,r}=w_{l,r} imes(sum_{l'le r'}f_{i-1,l',r'}-sum_{l'le r'<l}f_{i-1,l',r'}-sum_{r<l'le r'}f_{i-1,l',r'})
]
但这样的复杂度显然不太对劲,需要优化。
前缀和优化
首先我们发现(f_{i,l,r})这状态数已经达到了三维,显然从这里就需要开始改造。
因此给它做两次前缀和:
[g_{i,r}=sum_{l=1}^rf_{i,l,r}\
s_{i,j}=sum_{r=1}^jg_{i,r}
]
那么先前的转移式就可以写作:
[f_{i,l,r}=w_{l,r} imes(s_{i-1,m}-s_{i-1,l-1}-s_{i-1,m-r})
]
然后只考虑把(g_{i,r})的转移式给写开:
[egin{align}
g_{i,r}&=sum_{l=1}^rf_{i,l,r}\
&=sum_{l=1}^rw_{l,r} imes(s_{i-1,m}-s_{i-1,l-1}-s_{i-1,m-r})\
&=q_{m-r} imes((sum_{l=1}^rq_{l-1}) imes(s_{i-1,m}-s_{i-1,m-r})-sum_{l=1}^{r}q_{l-1} imes s_{i-1,l-1})
end{align}
]
于是只要在枚举(r)的同时维护好(sum_{l=1}^rq_{l-1})和(sum_{l=1}^{r}q_{l-1} imes s_{i-1,l-1})就可以直接转移了。
最终的答案显然就是(s_{n,m})。
代码:(O(nm))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1500
#define K 100000
#define X 1000000007
#define C(x,y) (1LL*Fac[x]*IFac[y]%X*IFac[(x)-(y)]%X)//组合数
using namespace std;
int n,m,k,p,q[K+5],f[2][N+5][N+5],g[N+5][N+5],s[N+5][N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int Fac[N+K],IFac[K+5];I void InitFac()//预处理阶乘和阶乘逆元
{
RI i;for(Fac[0]=i=1;i<=k;++i) Fac[i]=1LL*Fac[i-1]*i%X;
for(IFac[i=k]=QP(Fac[k],X-2);i;--i) IFac[i-1]=1LL*IFac[i]*i%X;
}
int main()
{
RI i,j,x,y;scanf("%d%d%d%d%d",&n,&m,&x,&y,&k),p=1LL*x*QP(y,X-2)%X,InitFac();
for(i=0;i<=k;++i) q[i]=1LL*C(k,i)*QP(p,i)%X*QP((1-p+X)%X,k-i)%X;//预处理k次中恰好成功i次的概率
for(g[0][m]=s[0][m]=i=1;i<=n;++i)//枚举每一行DP
{
for(x=y=j=0;j<=m;x=(x+q[j])%X,y=(1LL*q[j]*s[i-1][j]+y)%X,++j)//枚举j同时维护好和
g[i][j]=1LL*q[m-j]*(1LL*x*(s[i-1][m]-s[i-1][m-j]+X)%X-y+X)%X;//转移
for(s[i][0]=g[i][0],j=1;j<=m;++j) s[i][j]=(s[i][j-1]+g[i][j])%X;//对g前缀和求出s
}return printf("%d
",s[n][m]),0;
}