CodeForces 708E Student‘s Camp 题解

题目传送门

题目大意: 有一个 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(1p)ki,可以预处理出 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(l1)A(mr)[l,r][l,r]=ϕfi1,l,r

考虑优化这个dp,注意到下面的限制本质是 l ′ ≤ r   & &   r ′ ≥ l l'\leq r~\&\&~r'\geq l lr && rl,那么就可以将转移表示成另外一种形式:
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=Al1Amr((1lrmfi1,l,r)(1lr<lfi1,l,r)(r<lrmfi1,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=1lrpfi,l,r , sufi,p=plrmfi,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=Al1Amr(prei1,mprei1,l1sufi1,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=1pr=lpAl1Amr(prei1,mprei1,l1sufi1,r+1)

不难想到 p r e i , p pre_{i,p} prei,p 是可以从 p r e i , p − 1 pre_{i,p-1} prei,p1 递推过来的,即:
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,p1+l=1pAl1Amp(prei1,mprei1,l1sufi1,p+1)=prei,p1+(Ampprei1,ml=1pAl1)(Ampl=1pAl1prei1,p1)(Ampsufi1,p+1l=1pAl1)

不难发现三个 ∑ \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]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值