【题解】LOJ6300. 「CodePlus 2018 3 月赛」博弈论与概率统计

首先转化成总和除以方案数的形式。

先想想一条路径的答案是什么,显然是 \(n - m + cnt\),其中 \(cnt\) 为没用的输局。

这种东西显然非常能够转化成网格计数。起点为 \((0, 0)\) 终点为 \((n, m)\) 赢一局向右一步,输一局向上一步。你会发现 \(cnt\) 相当于最大的 \(k\) 满足路径顶到了 \(y = x + k\),为了方便处理我们拆贡献成 \(y = x + 1 \sim y = x + k\) 的贡献。

于是我们的问题变成了求顶到 \(y = x + k\) 的路径数量。这是经典的反射计数,取第一次顶到 \(y = x + k\) 的位置到 \((n, m)\) 的部分对称一下,转化为求从 \((0, 0) \to (m - k, n + k)\) 的路径数量,这是简单的。

那么分为 \(n \geq m\)\(n < m\) 两种情况考虑。

对于前者,贡献为 \((n - m)\binom{n + m}{n} + \sum_{i=1}^m\binom{n + m}{m - i} = (n - m)\binom{n + m}{n} + \sum_{i = 0}^{m - 1}\binom{n + m}{i}\)

对于后者,需要满足 \(cnt \geq m - n\),贡献为 \((n-m)\binom{n + m}{n} + \sum_{i = m - n + 1}^{m}\binom{n + m}{m - i} = (n - m)\binom{n + m}{n} + \sum_{i = 0}^{n - 1}\binom{m + n}{i}\)

由于有多组测试数据,所以直接上莫队优化。

注意莫队实现的时候不能直接将 \(n, m\) 拿去莫队,而是要将 \(n - 1\)\(m - 1\)\(n + m\) 拿去莫队。然后加加减减的部分我们愉快地推一下式子就可以啦。

可以通过这个神秘做法做到 \(O(mlog^2m)\)

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i ++)
#define fro(i, a, b) for (int i = (a); i >= b; i --)
#define INF 0x3f3f3f3f
#define eps 1e-6
#define lowbit(x) (x & (-x))
#define reg register
#define IL inline
typedef long long LL;
typedef std::pair<int, int> PII;
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
    return x * f;
}
// mt19937_64 sj(chrono::steady_clock::now().time_since_epoch().count());
// uniform_int_distribution<LL> u0(0, 1ll << 60);

const int N = 250010, B = 500, Mod = 1e9 + 7;
int n, m, p, q, inv2;
int ans, l, r, Ans[N];
int fac[N], infac[N];

struct Query { 
    int l, r, n, m, id; 
    bool operator < (const Query &t) const {
        return l / B == t.l / B ? r < t.r : l / B < t.l / B;
    }
};
vector<Query> Q;

IL int qmi(int a, int b = Mod - 2, int p = Mod) {
    int res = 1;
    while (b) {
        if (b & 1) res = 1ll * res * a % p;
        b >>= 1; a = 1ll * a * a % p;
    }
    return res;
}

void init() {
    fac[0] = infac[0] = 1; inv2 = qmi(2);
    for (int i = 1; i <= N - 10; i ++) {
        fac[i] = 1ll * fac[i - 1] * i % Mod;
        infac[i] = qmi(fac[i]);
    }
}

IL int C(int n, int m) {
    if (n < 0 || m < 0 || n < m) return 0;
    return 1ll * fac[n] * infac[m] % Mod * infac[n - m] % Mod;
}

void addl() {
    ans = (ans + C(r, l + 1)) % Mod;
}

void dell() {
    ans = ((ans - C(r, l)) % Mod + Mod) % Mod;
}

void addr() {
    ans = ((2ll * ans % Mod - C(r, l)) % Mod + Mod) % Mod;
}

void delr() {
    ans = 1ll * (ans + C(r - 1, l)) % Mod * inv2 % Mod;
}

int main() {
    q = read(), p = read(); init();
    for (int i = 1; i <= q; i ++) {
        int n = read(), m = read();
        if (n < m) Q.push_back({n - 1, n + m, n, m, i});
        else Q.push_back({m - 1, n + m, n, m, i});
    }
    sort(Q.begin(), Q.end()); 
    l = 1, r = 0; ans = 1;
    for (auto [L, R, n, m, id] : Q) {
        while (r <= R) addr(), r ++;
        while (l >= L) dell(), l --;
        while (r > R) delr(), r --;
        while (l < L) addl(), l ++; 
        if (n > m) Ans[id] = 1ll * (n - m) * C(n + m, n) % Mod;
        Ans[id] = 1ll * (Ans[id] + ans) * qmi(C(n + m, n)) % Mod;
    }
    for (int i = 1; i <= q; i ++) printf("%d\n", Ans[i]);
    return 0;
}
posted @ 2025-11-14 20:05  はなこくん  阅读(4)  评论(0)    收藏  举报