CF708E Student's Camp

Student's Camp

有一个 ((n + 2) × m) 的长方形,除了第一行和最后一行,其他每一行每一天最左边和最右边未被摧毁的格子都有概率 (P) 被摧毁(每天概率相同),每行之间独立且左边和右边独立,求 (k) 天之后最上面一行与最下面一行四联通的概率。

答案对 (10^9 + 7) 取模。

(n, m ≤ 1500)

题解

https://www.luogu.com.cn/blog/hotwords/solution-cf708e

首先转化题意。题目中已经限定了最高和最低一行的方块不会被吹走,所以整个建筑「不连通」只可能是被「上下劈开」,不可能是被「左右劈开」。也就是说,整个建筑「连通」,当且仅当对于所有相邻的两行,它们最后剩下的方块连通。

考虑 DP。设 (f(i,l,r)) 表示第 (i) 行剩下 ([l,r]) 的方块且建筑不倒的概率。

先考虑某一行剩下 ([l,r]) 的方块的概率。

(D(i)) 表示 (k) 次「吹风」中 (i) 次成功的概率,显然

[D(i) = inom k i p^i(1-p)^{k-i} ]

可以 (O(k)) 预处理求出所有的 (D(i))

(P(l,r) (1 le l le r le m)) 表示某一行只剩下 ([l,r]) 的砖块的概率,由于左右两边相互独立,可以得到 $$ P(l,r) = D(l-1) D(m-r) $$

接下来考虑如何从第 (i-1) 行转移。

由前面的分析,第 (i) 行最后剩下的方块所占的区间要和第 (i-1) 行的区间有交,即

[f(i,l,r) = P(l,r) sum_{[l',r']cap[l,r] e varnothing} f(i-1,l',r') ]

边界是 (f(0,1,m) = 1) ,答案即为所有 (f(n,l,r)) 的和。

这样状态数为 (O(nm^2)) ,转移复杂度 (O(m^2)) ,需要优化。

考虑问题的反面,即 ([l,r])([l',r']) 没有交集。容易发现,这只有两种互斥的情况:要么 (r' lt l) ,要么 (l' gt r)

于是,我们得到:

[f(i,l,r) = P(l,r) left[ sum_{l' le r'} f(i-1,l',r') - sum_{r' lt l} f(i-1,l',r') - sum_{l' gt r} f(i-1,l',r') ight] ]

发现只要求出三个求和号的部分就能转移了,即

[egin{aligned} F(i) &= sum_{l le r} f(i,l,r) \ L(i,x) &= sum_{l le r lt x} f(i,l,r) \ R(i,x) &= sum_{r ge l gt x} f(i,l,r) \ f(i,l,r) &= P(l,r) Big[ F(i-1) - L(i-1,l) - R(i-1,r) Big] end{aligned} ]

注意其中 (F(i)) 的意义,它表示前 (i) 行连通的概率,那么答案就是 (F(n))

细心的同学可能已经发现了, (L(i,x))(R(i,x)) 在转移、形式和结果上都是高度对称的。事实上,把 (L(i,x)) 左右翻转就可以得到 (R(i,x)) ,即 (L(i,x)=R(i,m-x+1)) 。于是我们只要讨论 (L(i,x)) 的处理就可以了。

由于 (f(i,l,r)) 会对所有 (x gt r)(L(i,x)) 产生贡献,我们可以先记 (S_L(i,r)) 表示所有右端点为 (r)(f(i,l,r)) 之和,则对 (S_L) 计算前缀和即可得到 (L) ,用公式表达就是:

[egin{aligned} S_L(i,r) &= sum_{l le r} f(i,l,r) \ L(i,x) &= sum_{r lt x} S_L(i,r) end{aligned} ]

同时我们可以发现,所有的 (S_L(i,r)) 之和就等于 (F(i)) ,这样就把 (F(i)) 也一并解决了。

接下来考虑如何计算 (S_L(i,r)) ,把 (f(i,l,r)) 的转移式代入:

[egin{aligned} S_L(i,r) &= sum_{l le r} f(i,l,r) \ &= sum_{l le r} P(l,r) Big[ F(i-1) - L(i-1,l) - R(i-1,r) Big] \ &= D(m-r) sum_{l le r} D(l-1) Big[ F(i-1) - L(i-1,l) - R(i-1,r) Big] \ &= D(m-r) left( Big[ F(i-1)-R(i-1,r) Big] sum_{l le r} D(l-1) - sum_{l le r} D(l-1) L(i-1,l) ight) end{aligned} ]

到这里思路已经很清晰了,对 (D(l-1))(D(l-1) L(i-1,l)) 做前缀和即可实现 (O(1)) 转移,最后答案即为 (F(n)) 。边界同样是 (f(0,1,m)=1) ,即 (S_L(0,m)=1)

总时间复杂度 (O(nm+k)) ,空间复杂度可以用滚动数组优化至 (O(m+k))

CO int N=1.5e3+10,M=1e5+10;
int fac[M],ifac[M];
int D[N],sumD[N];
int f[N][N];

IN int C(int n,int m){
	return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int main(){
	int n=read<int>(),m=read<int>();
	int A=read<int>(),B=read<int>(),P=mul(A,fpow(B,mod-2));
	int K=read<int>();
	fac[0]=1;
	for(int i=1;i<=K;++i) fac[i]=mul(fac[i-1],i);
	ifac[K]=fpow(fac[K],mod-2);
	for(int i=K-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
	for(int i=0;i<=m and i<=K;++i)
		D[i]=mul(C(K,i),mul(fpow(P,i),fpow(1+mod-P,K-i)));
	sumD[0]=D[0];
	for(int i=1;i<=m;++i) sumD[i]=add(sumD[i-1],D[i]);
	f[0][m]=1;
	for(int i=1;i<=n;++i){
		int tot=f[i-1][m];
		int DL=0;
		for(int j=1;j<=m;++j){
			DL=add(DL,mul(D[j-1],f[i-1][j-1]));
			int tmp=add(mul(tot+mod-f[i-1][m-j],sumD[j-1]),mod-DL);
			f[i][j]=add(f[i][j-1],mul(D[m-j],tmp));
		}
	}
	printf("%d
",f[n][m]);
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/12752725.html