题意:给定一个\(H*W(H,W<=1e5)\)的棋盘,棋盘上只有\(N(N<=2000)\)个格子是黑色的,其他格子都是白色的.在棋盘左上角有一个卒,每一步可以向右或者向下移动一格,并且不能移动到黑色格子中.求这个卒从左上角移动到右下角,一共有多少种可能的路线.答案对\(1e9+7\)取模.
分析:这种题目直接算肯定不好算,因为黑色格子远远少于白色格子,考虑\(ans=\)所有方案-不合法方案.
首先从左上角走到右下角的总方案数为\(C_{H-1+W-1}^{H-1}\).
将n个黑色格子按照横坐标从小到大排序.设\(f[i]\)表示从左上角走到(排序后的)第i个黑色格子并且途中不经过其它黑色格子的方案数.
\(f[i]=C_{x_i-1+y_i-1}^{x_i-1}-\sum_{j=0}^{i-1}f[j]*C_{x_i-x_j+y_i-y_j}^{x_i-x_j}\)
很好理解,用到i的所有方案数减去j到i的所有方案数(因为j也是黑色格子,所以j到i的所有方案都是不合法的),剩下的就是合法的到i的方案数了.
我们把右下角看作第n+1个黑色格子,于是\(ans=f[n+1]\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=200005;
const int mod=1e9+7;
ll jc[N],inv[N],f[N];
struct ppx{
int x,y;
}a[2005];
inline bool cmp(const ppx &x,const ppx &y){return x.x==y.x?x.y<y.y:x.x<y.x;}
inline ll ksm(int a,int b){
ll cnt=1;
while(b){
if(b&1)cnt=(1ll*cnt*a)%mod;
a=(1ll*a*a)%mod;
b>>=1;
}
return cnt;
}
inline ll C(int n,int m){return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;}
int main(){
jc[0]=1;inv[0]=1;
for(int i=1;i<=200000;++i){
jc[i]=(jc[i-1]*i)%mod;
inv[i]=ksm(jc[i],mod-2);
}
int h=read(),w=read(),n=read();
for(int i=1;i<=n;++i)a[i].x=read(),a[i].y=read();
sort(a+1,a+n+1,cmp);a[n+1].x=h;a[n+1].y=w;
for(int i=1;i<=n+1;++i){
f[i]=C(a[i].x+a[i].y-2,a[i].x-1);
for(int j=1;j<i;++j)
f[i]=(f[i]-1ll*f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x))%mod;
}
printf("%lld\n",(f[n+1]+mod)%mod);
return 0;
}