40 CF559C Gerald and Giant Chess 题解
CF559C Gerald and Giant Chess
题面
给定一个 \(H \times W\) 的棋盘,棋盘上只有 \(N\) 个格子是黑色的,其他格子都是白色
在棋盘左上角有一个卒,每一步可以向下或者向右移动一格,并且不能移动到黑色格子中。
求这个卒从左上角移动到右下角,一共有多少种可能的路线?
\(1 \le H,W \le 10^5, \ 1 \le N \le 2000\) 输出答案对 \(10^9 + 7\) 取模的结果即可
题解
棋盘上的白色格子很多,但是黑色格子只有 \(2000\) 个,所以我们考虑怎样利用黑色格子计数
这种计数类问题要做到不重不漏,要保证每一类情况是互斥的,不能有重合,并且能包含所有情况
为了方便,我们把右下角格子看做第 n + 1 个黑色格子
先将黑色格子按照横纵坐标排序,然后进行dp
设 \(f(i)\) 表示从左上角走到第 \(i\) 个黑色格子并且中间不经过其他黑色格子的方案数,初始状态 \(f[0] = 1\) ,目标状态 \(f[N + 1]\)
转移
\[f(i) = \binom {x_i - 1 + y_i - 1} {x_i - 1} - \sum_{j = 1}^{i - 1} f(j) \times \binom {x_i - x_j + y_i - y_j} {x_i - x_j}
\]
\(j\) 满足 \(x_j \le x_i \and y_j \le y_i\)
时间复杂度为 \(O(n^2)\)
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 2010, M = 2e5 + 10;
const int mod = 1e9 + 7;
int H, W, n;
pair <int, int> a[N];
ll f[N], fac[M], inv[M];
int pw (int a, int b) {
ll res = 1, t = a;
while (b) {
if (b & 1) {
res *= t;
res %= mod;
}
t *= t;
t %= mod;
b >>= 1;
}
return res;
}
void init () {
int mn = H + W;
fac[0] = 1, inv[0] = 1;
for (int i = 1; i <= mn; i ++) {
fac[i] = fac[i - 1] * i % mod;
}
inv[mn] = pw (fac[mn], mod - 2);
for (int i = mn - 1; i >= 1; i --) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
}
int calc (int x, int y) {
if (y > x) return 0;
return (ll)fac[x] * inv[y] % mod * inv[x - y] % mod;
}
int main () {
cin >> H >> W >> n;
init ();
for (int i = 1; i <= n; i ++) {
scanf ("%d%d", &a[i].first, &a[i].second);
}
sort (a + 1, a + 1 + n);
a[n + 1] = {H, W};
for (int i = 1; i <= n + 1; i ++) {
f[i] = calc (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[j] * calc (a[i].first - a[j].first + a[i].second - a[j].second, a[i].first - a[j].first) % mod;
}
f[i] = (f[i] % mod + mod) % mod;
}
cout << f[n + 1] << endl;
return 0;
}