【题解】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;
}

浙公网安备 33010602011771号