题目链接
Problem
从(1, 1)走到(n, m),只能往右走或者下走,有k个点不能走,问合法的路径条数有多少条?
数据范围:
1<=h, w<=1e5, 1<=n<=2000.
Input
3 4 2
2 2
2 3
Output
2
Ideas
前置技能:从(x1, y1)走到(x2, y2)的方案数:C(x2 - x1 + y2 - y1, x2 - x1), 为什么? 从(x1, y1)走到(x2, y2),一共要走x2 - x1 + y2 - y1这些步,从这里面选x2 - x1步往右走就是总的方案数。
dp[i]表示从(1, 1)到第i个点的合法的路径,f(i, j)表示从第i个点到第j个点的路径条数(不一定合法),那么当k=4, 第0个点是起点,第5个点是终点的时候,我们不合法的方案数就是dp[4]f(4, 5) + dp[3] f(3, 5) + dp[2] * f(2, 5) + dp[1] * f(1, 5)。
dp[i]应该怎么求呢? 首先dp[1]很好求,就是f(0, 1)。dp[2] = f(0, 2) - dp[1] * f(1, 2)。 dp[3] = f(0, 3) - dp[1] * f(1, 3) - dp[2] * f(2, 3)。…….
dp[i] = get_num(0, i) - sum{dp[j]*num[i][j]}, j = 1,2,…,i-1.
最后用总的方案数-sum{dp[i]}, i = 1, 2,….k。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 100;
const int mod = 1e9 + 7;
ll dp[maxn], mul[maxn], inv[maxn];
//mul 前缀积
//inv[i] mul[i]的逆元
struct node {
int x, y;
bool operator < (const node &a) const {
if(x == a.x) return y < a.y;
return x < a.x;
}
} p[maxn];
ll pow_q(ll a, int k) {
ll ans = 1;
while(k) {
if(k & 1) {
ans *= a;
ans %= mod;
}
a = a * a % mod;
k >>= 1;
}
return ans;
}
void init() {
mul[0] = 1;
inv[0] = 1;
for(int i = 1; i < maxn; i++) {
mul[i] = mul[i - 1] * i;
mul[i] %= mod;
inv[i] = pow_q(mul[i], mod - 2);
}
}
ll GetC(int n, int m) {
return mul[n] * inv[m] % mod * inv[n - m] % mod;
}
ll GetPathNum(int ex, int ey, int sx, int sy) {
return GetC(ex - sx + ey - sy, ex - sx);
}
int main()
{
init();
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for(int i = 0; i < k; i++) {
scanf("%d %d", &p[i].x, &p[i].y);
}
sort(p, p + k);
ll ans = GetPathNum(n, m, 1, 1);
for(int i = 0; i < k; i++) {
dp[i] = GetPathNum(p[i].x, p[i].y, 1, 1);
for(int j = 0; j < i; j++) {
if(p[j].x <= p[i].x && p[j].y <= p[i].y) {
dp[i] = dp[i] - (dp[j] * GetPathNum(p[i].x, p[i].y, p[j].x, p[j].y)) % mod;
dp[i] = (dp[i] + mod) % mod;
}
}
ans = (ans + mod - (dp[i] * GetPathNum(n, m, p[i].x, p[i].y) % mod)) % mod;
}
printf("%lld\n", ans);
}