首先\(k\)相对\(n,m\)要大许多,肯定不会把它放到\(DP\)状态中。
因此我们只需要考虑最终状态,那就只需要设\(q_i\)表示在\(k\)轮删除操作中恰好进行\(i\)次的概率:
由于前后的删除相对独立,因此设\(w_{l,r}\)表示最后被删得恰好只剩\([l,r]\)的概率,就有:
这样一来,如果我们设\(f_{i,l,r}\)表示第\(i\)行最终被删得只剩\([l,r]\)且前\(i\)行连通的概率,就只需考虑这一行与上一行是否连通:
两个区间有交,可以容斥用总方案数减去无交的方案数:
但这样的复杂度显然不太对劲,需要优化。
首先我们发现\(f_{i,l,r}\)这状态数已经达到了三维,显然从这里就需要开始改造。
因此给它做两次前缀和:
那么先前的转移式就可以写作:
然后只考虑把\(g_{i,r}\)的转移式给写开:
于是只要在枚举\(r\)的同时维护好\(\sum_{l=1}^rq_{l-1}\)和\(\sum_{l=1}^{r}q_{l-1}\times s_{i-1,l-1}\)就可以直接转移了。
最终的答案显然就是\(s_{n,m}\)。
#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\n",s[n][m]),0;
}
原文:https://www.cnblogs.com/chenxiaoran666/p/CF708E.html