「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\) 方程:

\[\begin{aligned} dp_{i, l, r} & = P(l, r) \sum dp_{i - 1, l ^ \prime, r ^ \prime}, [l ^ \prime, r ^ \prime] \cap [l, r] \ne \varnothing \\ & = P(l, r) \left( \sum_{l ^ \prime < r ^ \prime} dp_{i - 1, l ^ \prime,r ^ \prime} - \sum_{r ^ \prime < l} dp_{i - 1, l ^ \prime, r ^ \prime} - \sum_{r < l ^ \prime} dp_{i - 1, l ^ \prime, r ^ \prime} \right) \end{aligned} \]

写成这样后,我们可以想到用前缀和来优化转移:
\(F_{i, r} = \sum_{l \le r} dp_{i, l, r}, S_{i, r} = \sum_{j \le r} F_{i, j}\)
考虑到左右的对称性,我们也可以用这两个东西算后缀形式的和。
那么我们上面那个式子就可以进一步写成:

\[dp_{i, l, r} = P(l, r) \left( S_{i - 1, m} - S_{i - 1, l - 1} - S_{i - 1, m - r} \right) \]

那么就可以推出:

\[\begin{aligned} F_{i, r} & = \sum_{l \le r} dp_{i, l, r} \\ & = \sum_{l \le r} P(l, r) \left( S_{i - 1, m} - S_{i - 1, l - 1} - S_{i - 1, m - r} \right) \\ & = \sum_{l \le r} q_{l - 1} \times q_{m -r} \left( S_{i - 1, m} - S_{i - 1, l - 1} - S_{i - 1, m - r} \right) \\ & = q_{m - r} \left[ \sum_{l \le r} q_{l - 1} \left( S_{i - 1, m} - S_{i - 1, l - 1} \right) - S_{i - 1, m - r} \sum_{l \le r} q_{l - 1} \right] \end{aligned} \]

不难看出其中的前缀和优化,那么我们就可以算出 \(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;
}
posted @ 2020-05-09 16:38  Sangber  阅读(152)  评论(2编辑  收藏  举报