把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF708E】Student's Camp(动态规划)

点此看题面

  • 初始有一张\((n+2)\times m\)的网格图。
  • 共操作\(k\)轮,每轮除第一行和最后一行外每行的第一个格子和最后一个格子均有\(p=\frac ab\)的概率被删除。
  • 求最终图连通的概率。
  • \(n,m\le1500,k\le10^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}\times p_{m-r} \]

这样一来,如果我们设\(f_{i,l,r}\)表示第\(i\)行最终被删得只剩\([l,r]\)且前\(i\)行连通的概率,就只需考虑这一行与上一行是否连通:

\[f_{i,l,r}=w_{l,r}\times\sum_{[l,r]\cap[l',r']\not=\emptyset}f_{i-1,l',r'} \]

两个区间有交,可以容斥用总方案数减去无交的方案数:

\[f_{i,l,r}=w_{l,r}\times(\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}\times(s_{i-1,m}-s_{i-1,l-1}-s_{i-1,m-r}) \]

然后只考虑把\(g_{i,r}\)的转移式给写开:

\[\begin{align} g_{i,r}&=\sum_{l=1}^rf_{i,l,r}\\ &=\sum_{l=1}^rw_{l,r}\times(s_{i-1,m}-s_{i-1,l-1}-s_{i-1,m-r})\\ &=q_{m-r}\times((\sum_{l=1}^rq_{l-1})\times(s_{i-1,m}-s_{i-1,m-r})-\sum_{l=1}^{r}q_{l-1}\times s_{i-1,l-1}) \end{align} \]

于是只要在枚举\(r\)的同时维护好\(\sum_{l=1}^rq_{l-1}\)\(\sum_{l=1}^{r}q_{l-1}\times 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\n",s[n][m]),0;
}
posted @ 2021-04-02 11:05  TheLostWeak  阅读(92)  评论(0编辑  收藏  举报