CF708E Student's Camp

Problem

CF708E Student's Camp

Analysis

正解思路

首先,分析题目的操作性质,发现每行最后形成的一定是一段区间。不妨考虑计算 \((l,r)\) 的形成概率,\(g_{l,r}=\binom{k}{l-1}p^{l-1}(1-p)^{k-l+1}\binom{k}{m-r}p^{m-r}(1-p)^{k+r-m}\)

进一步考虑形成两个连通块的限制条件:两个连通的交接两行的最终区间无交。很容易去想到设计一个 dp:\(dp_{i,l,r}\) 为考虑前 \(i\) 行第 \(i\) 行状态为 \([l,r]\) 且最终不分裂的概率。转移比较显然 \(dp_{i,l,r}=g_{l,r}\sum_{[l',r']\cap [l,r]\ne\emptyset}dp_{i-1}\)

可是,这是 \(O(nm^4)\) 的时间复杂度。考虑简单容斥,转移时用总方案减去非法方案,\(dp_{i,l,r}=g_{l,r}(\sum_{l'\le r'}dp_{i-1,l',r'}-\sum_{r'<l}dp_{i-1,l',r'}-\sum_{r<l'}dp_{i-1,l',r'})\)

这三项分别写为 \(F_i=\sum_{l\le r}dp_{i,l,r}\)\(L_{i,j}=\sum_{l\le r<j}dp_{i,l,r}\)\(R_{i,j}=\sum_{j<l\le r}dp_{i,l,r}\)

所以 \(dp_{i,l,r}=g_{l,r}(F_{i-1}-L_{i-1,l}-R_{i-1,r})\)

剩余的优化部分实在太 shit,请参考这篇题解

错因总结

这题我也太糖了,我居然天真的以为只有缺失一行就会形成两个连通块,但其实只要相邻两行无交集就会形成两个连通块。

其实 dp 思路跟我想的差不多,再加前缀和优化就可过。

AC Code

点击查看代码
#include <bits/stdc++.h>
#define ll long long 
#define ull unsigned long long 
#define i128 __int128
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define mk make_pair
#define INF 0x3f3f3f3f
#define INFx 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 1e5 + 10, M = 1510;
const int mod = 1e9 + 7;

int n, m, a, b, p, k;
int f[N], finv[N];
int D[M]; // D[i] = \binom{k}{i} * p^{i} * (1 - p)^{k - i}
int SD[M]; // SD[r] = \sum_{l = 1}^{r} D[l - 1]
int SDL[M]; // SDL[r] = \sum_{l = 1}^{r} D[l - 1] * L[i - 1][l] 
int F[M]; // F[i] = sum_{r = 1}^{m} SL[i][r]
int L[M]; // L[x] = \sum_{r = 1}^{x - 1} SL[i][r]
int R[M]; // R[x] = L[i][m - x + 1] 
int SL[M][M]; // SL[i][r] = D[m - r] * [(F[i - 1] - R[r]) * SD[r] - SDL[r]]

int fp(int a, int b) {
    int mul = 1;
    while (b) {
        if (b & 1) mul = 1ll * mul * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    }
    return mul;
}

void init(int n) {
    f[0] = finv[0] = 1;
    for (int i = 1; i <= n; i ++) {
        f[i] = 1ll * f[i - 1] * i % mod;
        finv[i] = fp(f[i], mod - 2);
    }
}

int C(int n, int m) {
    if (n < m) return 0;
    return 1ll * f[n] * finv[n - m] % mod * finv[m] % mod;
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    cin >> n >> m >> a >> b >> k;

    init(k);
    p = 1ll * a * fp(b, mod - 2) % mod;

    for (int i = 0; i <= min(m, k); i ++) {
        D[i] = 1ll * C(k, i) * fp(p, i) % mod * fp((1 - p + mod) % mod, k - i) % mod;
    } 

    SL[0][m] = 1;

    // SD[r]
    SD[0] = D[0];
    for (int r = 1; r <= m; r ++) {
        SD[r] = (1ll * SD[r - 1] + D[r]) % mod;
    }

    for (int i = 1; i <= n; i ++) {
        // L[x] & R[x]
        for (int x = 1; x <= m; x ++) {
            L[x] = (1ll * L[x - 1] + SL[i - 1][x]) % mod;
            R[m - x] = L[x];
        }
        // SDL[r] 
        for (int r = 1; r <= m; r ++) {
            SDL[r] = (1ll * SDL[r - 1] + 1ll * D[r] * L[r] % mod) % mod;
        }
        // F[i]
        for (int r = 1; r <= m; r ++) {
            F[i] = (1ll * F[i] + SL[i - 1][r]) % mod;
        }
        // SL[i][r]
        for (int r = 1; r <= m; r ++) {
            int res = ((1ll * F[i] - R[r] + mod) % mod * SD[r - 1] % mod - SDL[r - 1] + mod) % mod;
            SL[i][r] = 1ll * D[m - r] * res % mod;
        }
    }
    
    int ans = 0;
    for (int r = 1; r <= m; r ++) {
        ans = (1ll * ans + SL[n][r]) % mod;
    }

    cout << ans << '\n';

    return 0;
}
posted @ 2026-03-02 16:20  KenopsiaMind  阅读(0)  评论(0)    收藏  举报