描述
有一个
h×w
的棋盘,需要从左上角走到右下角。每次只能向右或者向下走一步。其中有
n
个格子被标记成不能就经过,这些格子的坐标为
(1≤h,w≤105,1≤n≤2000,1≤ri≤j,1≤ci≤w,ansmod109+7)
思路
一开始的思路是容斥原理,但是公式推了半天都推不出来。最后经过指导得出了解法。
记从
(x1,y1)
到
(y1,y2)
的方案数为
f(x1,y1,x2,y2)=c(x2−x1+y2−y1,y2−y1)
。
记
dp(x,y)
为到从起点到
(x,y)
点并且不经过被标记的点的方案数。
直接算
dp(x,y)
确实不好算,我们可以算出总共的,再减去不合法的。总共的值为
f(1,1,x,y)
,对于
(1,1)
到
(x,y)
之间的所有的被标记的点
(ri,ci)
那么一它为第一个经过的被标记的点的方案数是
dp(ri,ci)×f(ri,ci,x,y)
。
那么
dp(x,y)=f(1,1,x,y)−∑(dp(ri,ci)×f(ri,ci,x,y))
于是我们要计算的是 dp(h,w) ;
code
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1E9+7;
const int maxn=300005;
long long fac[maxn];
long long inv[maxn];
/*=================================================*/
long long power(long long a,long long p) {
long long res=1;
while(p) {
if(p&1)
res=(res*a)%MOD;
a=(a*a)%MOD;
p>>=1;
}
return res;
}
long long Inv(long long a) {
return power(a,MOD-2);
}
void init() {
fac[0]=inv[0]=1;
for(long long i=1;i<300005;i++) {
fac[i]=fac[i-1]*i;
fac[i]%=MOD;
inv[i]=Inv(fac[i]);
}
}
long long C(long long n,long long m) {
if(n<0||m<0)
return 0;
long long res=fac[n];
res%=MOD;
res*=inv[m];
res%=MOD;
res*=inv[n-m];
res%=MOD;
return res;
}
/*=================================================*/
struct Node {
int x, y;
Node() {}
Node(int _x, int _y) {
x = _x;
y = _y;
}
}p[2005];
bool cmp (Node a, Node b) {
if (a.x == b.x) return a.y < b.y;
else return a.x < b.x;
}
int h, w, n;
long long res = 0;
long long dp[2005];
int main () {
init();
scanf("%d%d%d", &h, &w, &n);
p[0] = Node(h, w);
for (int i=1; i<=n; i++) {
scanf ("%d%d", &p[i].x, &p[i].y);
}
sort(p, p+n+1, cmp);
dp[0] = C(p[0].x + p[0].y - 2, p[0].x-1);
for (int i=1; i<=n; i++) {
dp[i] = C(p[i].x - 1 + p[i].y - 1, p[i].x - 1);
res = 0;
for (int j=0; j<i; j++) {
if (p[j].x <= p[i].x && p[j].y <= p[i].y) {
res = (res + (dp[j] * C(p[i].x - p[j].x + p[i].y - p[j].y, p[i].x - p[j].x)) % MOD ) % MOD;
}
}
dp[i] = (dp[i] - res + MOD) % MOD;
}
printf("%d\n", dp[n]);
return 0;
}