「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} imes 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] e varnothing)
初始化 (dp_{0, 1, m} = 1),此时最后的答案就是 (sum dp_{n, l, r})
然后我们考虑优化。
我们可以改写一下上面那个 (dp) 方程:

[egin{aligned} dp_{i, l, r} & = P(l, r) sum dp_{i - 1, l ^ prime, r ^ prime}, [l ^ prime, r ^ prime] cap [l, r] e 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} ight) 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} ight) ]

那么就可以推出:

[egin{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} ight) \ & = sum_{l le r} q_{l - 1} imes q_{m -r} left( S_{i - 1, m} - S_{i - 1, l - 1} - S_{i - 1, m - r} ight) \ & = q_{m - r} left[ sum_{l le r} q_{l - 1} left( S_{i - 1, m} - S_{i - 1, l - 1} ight) - S_{i - 1, m - r} sum_{l le r} q_{l - 1} ight] 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
", s[n][m]);
    return 0;
}
原文地址:https://www.cnblogs.com/zsbzsb/p/12858592.html