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) 次成功的概率,显然
可以 (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(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)) 的意义,它表示前 (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) ,用公式表达就是:
同时我们可以发现,所有的 (S_L(i,r)) 之和就等于 (F(i)) ,这样就把 (F(i)) 也一并解决了。
接下来考虑如何计算 (S_L(i,r)) ,把 (f(i,l,r)) 的转移式代入:
到这里思路已经很清晰了,对 (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;
}