CF 559C Gerald and Giant Chess

题目描述

https://codeforces.com/problemset/problem/559/C

简要题意:给定一个 $n\times m$​ 的棋盘和 $k$​​​ 个黑色的格子,求从左上角到右下角且不经过任何黑色格子的方案数,只能向右或者向下走

$n,m\le 10^5,k\le 2000$

Solution

我们考虑容斥 $dp$,令 $f_i$ 表示从左上角到第 $i$​ 个黑色格子且中间不经过任何其它黑色格子的方案数

我们考虑如何从 $f_j$ 转移到 $f_i$,注意到任何一个不合法路径的存在第一个经过的黑色格子,我们可以按照第一个经过的黑色格子来区分所有不合法路径,那么我们枚举第一个黑色格子,容易得到 $f_i=\binom{x_i-1+y_i-1}{x_i-1}-\sum f_j\binom{x_i-x_j+y_i-y_j}{x_i-x_j}$​

时间复杂度 $O(n+k^2)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <algorithm>
#define maxn 200010
#define maxm 2010
#define ll long long
using namespace std;

const int p = 1000000007;

ll pow_mod(ll x, ll n) {
ll s = 1;
for (; n; n >>= 1, x = x * x % p)
if (n & 1) s = s * x % p;
return s;
}

ll fac[maxn], inv[maxn];
void init_C(int n) {
fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % p;
inv[n] = pow_mod(fac[n], p - 2); for (int i = n - 1; ~i; --i) inv[i] = inv[i + 1] * (i + 1) % p;
}

ll C(int n, int m) { return n < m ? 0 : fac[n] * inv[m] % p * inv[n - m] % p; }

int n, m, k;
pair<int, int> a[maxn];

int f[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m >> k; init_C(n + m);
for (int i = 1; i <= k; ++i) cin >> a[i].first >> a[i].second;
a[++k] = make_pair(n, m); sort(a + 1, a + k + 1);
for (int i = 1; i <= k; ++i) {
f[i] = C(a[i].first - 1 + a[i].second - 1, a[i].first - 1);
for (int j = 1; j < i; ++j) {
if (a[j].second > a[i].second) continue;
f[i] = (f[i] -
C(a[i].first - a[j].first + a[i].second - a[j].second, a[i].first - a[j].first) *
f[j]) % p;
}
} cout << (f[k] + p) % p << "\n";
return 0;
}