Description
给定一个 (H imes W) 的棋盘,有 (N) 个障碍格子,棋子每次可以向右或者向下移动一步,求从左上角到右下角有多少种不同的路线。(N le 2000)
Solution
由于 (N) 较小,考虑 dp,设 (f[i]) 表示走到第 (i) 个障碍点(且之前不经过其它任何障碍点)的方案数,则
[f[i]=g(x_i,y_i) - sum_{j o i} g(x_i-x_j+1,y_i-y_j+1 )cdot f[j]
]
其中 (j o i Leftrightarrow x_j le x_i and y_j le y_i),(g(x,y)) 表示无障碍情况下 ((1,1) o (x,y)) 的方案数,有
[g(x,y)=inom {x+y-2}{x-1}
]
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2005;
const int M = 200005;
const int mod = 1e9+7;
int f[N],frac[M],ifrac[M],h,w,x[N],y[N],n;
struct pii
{
int x,y;
bool operator < (const pii &obj)
{
if(x==obj.x) return y<obj.y;
return x<obj.x;
}
} buf[N];
int qpow(int p,int q)
{
return (q&1 ? p : 1) * (q ? qpow(p*p%mod,q>>1) : 1) % mod;
}
int inv(int p)
{
return qpow(p,mod-2);
}
int nCr(int n,int m)
{
return frac[n]*ifrac[m]%mod*ifrac[n-m]%mod;
}
int g(int x,int y)
{
return nCr(x+y-2,x-1);
}
signed main()
{
ios::sync_with_stdio(false);
cin>>h>>w>>n;
for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
frac[0]=ifrac[0]=1;
for(int i=1;i<M;i++) frac[i]=frac[i-1]*i%mod;
for(int i=1;i<M;i++) ifrac[i]=inv(frac[i]);
for(int i=1;i<=n;i++) buf[i].x=x[i], buf[i].y=y[i];
sort(buf+1,buf+n+1);
for(int i=1;i<=n;i++) x[i]=buf[i].x, y[i]=buf[i].y;
f[0]=1;
x[0]=y[0]=1;
x[n+1]=h;
y[n+1]=w;
for(int i=1;i<=n+1;i++)
{
f[i]=g(x[i],y[i]);
for(int j=1;j<i;j++)
{
if(x[i]>=x[j] && y[i]>=y[j]) f[i]-=g(x[i]-x[j]+1,y[i]-y[j]+1)*f[j];
f[i]%=mod;
f[i]+=mod;
f[i]%=mod;
}
}
cout<<f[n+1]<<endl;
}