https://codeforces.ml/contest/559/problem/C
题意:
从矩阵的左上角走到右下角,其中\(n\)个格子不能走,每次只能向右走或者向下走,问方案数。矩阵大小\(10^5 \times 10^5\),\(1 \leq n \leq 2000\)。
思路:
矩阵大小太大了,不能用常规方法去写。不能走的格子的数量很小,尝试从其中入手。
对于一个格子来说,它对答案的贡献是\((1\), \(1)\)到它的方案数 \(\times\) 它到\((h\), \(w)\)的方案数,但是发现左上的格子对于右下的格子是有影响的,因为左上的格子能走到右下的格子,我们在减去左上格子的贡献后,如果在减去右下格子的贡献时仍这样计算,就会重复计数。然后发现重复的部分时很容易计算的,将左上的格子作为中心点,计算出\((1\), \(1)\)到左上格子的方案,再计算左上格子到右下格子的方案,两个乘一下就是先前已经计算进去的贡献了,减掉即可。
有了解题思路,那么就先记录下不能走的格子的坐标,排个序,优先处理左上的格子。存在大量重复运算,用\(dp\)数组记录一下方案数,减少时间复杂度。计算组合数也要直接算,不要一个个慢慢乘。取模的时候要注意负数的情况。
#include <bits/stdc++.h>
using namespace std;
inline int rd() {
int f = 0; int x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
if (f) x = -x;
return x;
}
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 7;
const ll mod = 1e9 + 7;
int h, w, n;
ll inv[N];
int dp[N];
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
void chmod(ll &a) {
while (a < 0) a += mod;
while (a >= mod) a-= mod;
}
struct Node {
int x, y;
bool operator < (const Node &a) const {
if (x == a.x) return y < a.y;
return x < a.x;
}
}node[N];
void init() {
inv[0] = 1;
inv[1] = 1;
for (int i = 2; i < N; ++i) {
inv[i] = inv[i - 1] * (ll)i % mod;
}
}
ll C(int n, int m) {
return inv[n] * powmod(inv[m] * inv[n - m], mod - 2) % mod;
}
void solve() {
h = rd(); w = rd(); n = rd();
ll ans = C(h + w - 2, h - 1);
for (int i = 1; i <= n; ++i) {
node[i].x = rd();
node[i].y = rd();
}
sort(node + 1, node + 1 + n);
for (int i = 1; i <= n; ++i) {
int x = node[i].x, y = node[i].y;;
ll tmp = C(x + y - 2, x - 1);
for (int j = 1; j < i; ++j) {
int px = node[j].x, py = node[j].y;
if (px <= x && py <= y) {
tmp -= C(x - px + y - py, x - px) * dp[j] % mod;
chmod(tmp);
}
}
dp[i] = tmp;
tmp = tmp * C(h - x + w - y, h - x) % mod;
ans -= tmp;
chmod(ans);
}
printf("%lld\n", ans);
}
int main() {
int t = 1;
init();
while (t--) solve();
return 0;
}