「CF708E」Student's Camp
传送门
首先分析题意,存在两个联通分量等价于第 \(0\) 行和第 \(n + 1\) 行不连通。
然后我们可以发现这个风只会吹每一行的端点,也就是说每一行总是一段连续的区间。
那么不连通的情况就是存在相邻两行的区间不交。
那么首先考虑一个很显然的暴力:
设 \(dp_{i, l, r}\) 表示前 \(i\) 行联通,第 \(i\) 行的区间为 \([l, r]\) 的概率。
我们设 \(q_i = {k \choose i}p^i(1 - p) ^ {k - i}\) ,表示所有 \(k\) 次吹风中,有 \(i\) 次成功吹走砖块的概率。
那么某一行是 \([l, r]\) 的概率就是 \(P(l, r) = q_{l - 1} \times q_{m - r}\)。
所以有 \(dp_{i, l, r} = P(l, r) \sum dp_{i - 1, l ^ \prime, r ^ \prime}, [l ^ \prime, r ^ \prime] \cap [l, r] \ne \varnothing\)。
初始化 \(dp_{0, 1, m} = 1\),此时最后的答案就是 \(\sum dp_{n, l, r}\)
然后我们考虑优化。
我们可以改写一下上面那个 \(dp\) 方程:
写成这样后,我们可以想到用前缀和来优化转移:
设 \(F_{i, r} = \sum_{l \le r} dp_{i, l, r}, S_{i, r} = \sum_{j \le r} F_{i, j}\)。
考虑到左右的对称性,我们也可以用这两个东西算后缀形式的和。
那么我们上面那个式子就可以进一步写成:
那么就可以推出:
不难看出其中的前缀和优化,那么我们就可以算出 \(F(i, r)\) 和 \(S(i, r)\) 了。
最后的答案就是 \(S_{n, m}\)。
参考代码:
#include <cstdio>
const int _ = 1502, __ = 1e5 + 5, mod = 1e9 + 7;
int n, m, k, a, b, P, fac[__], ifc[__], p[__];
int dp[_][_], s[_][_], s1[_], s2[_];
int power(int x, int k) {
int res = 1;
for (; k; k >>= 1, x = 1ll * x * x % mod)
if (k & 1) res = 1ll * res * x % mod;
return res % mod;
}
int C(int N, int M) { return 1ll * fac[N] * ifc[M] % mod * ifc[N - M] % mod; }
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
scanf("%d%d%d%d%d", &n, &m, &a, &b, &k);
P = 1ll * a * power(b, mod - 2) % mod;
fac[0] = ifc[0] = 1;
for (int i = 1; i <= k; ++i) fac[i] = 1ll * i * fac[i - 1] % mod;
ifc[k] = power(fac[k], mod - 2);
for (int i = k; i; --i) ifc[i - 1] = 1ll * i * ifc[i] % mod;
for (int i = 0; i <= k; ++i)
p[i] = 1ll * C(k, i) * power(P, i) % mod * power((1 - P + mod) % mod, k - i) % mod;
dp[0][m] = s[0][m] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
s1[j] = (s1[j - 1] + 1ll * (s[i - 1][m] - s[i - 1][j - 1] + mod) % mod * p[j - 1] % mod) % mod;
s2[j] = (s2[j - 1] + p[j - 1]) % mod;
}
for (int j = 1; j <= m; ++j) {
dp[i][j] = 1ll * (s1[j] - 1ll * s[i - 1][m - j] * s2[j] % mod + mod) % mod * p[m - j] % mod;
s[i][j] = (s[i][j - 1] + dp[i][j]) % mod;
}
}
printf("%d\n", s[n][m]);
return 0;
}