Description
有一个h行w列的棋盘,里面有一些格子是不能走的,现在要求从左上角走到右下角的方案数。
Input
单组测试数据。
第一行有三个整数h, w, n(1 ≤ h, w ≤ 10^5, 1 ≤ n ≤ 2000),表示棋盘的行和列,还有不能走的格子的数目。
接下来n行描述格子,第i行有两个整数ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w),表示格子所在的行和列。
输入保证起点和终点不会有不能走的格子。
Output
输出答案对1000000007取余的结果。
Input示例
3 4 2
2 2
2 3
Output示例
2
解题思路
用ans[i]代表从起点走到第i个障碍格子且中间不经过其他障碍格子的方案数(终点也要算作障碍格子),
则ans[i]=Cx[i]−1x[i]+y[i]−1+∑ij=0(Cx[i]−x[j]x[i]−x[j]+y[i]−y[j]∗ans[j]).
具体思考过程:
要计算从坐上角走到右下角的方案数,我们可以借助不能走的地方来过渡进行计算,如图所示为一个4*8的网格,①②为不能走的格子,我们求解的思路可以转化为:
先求解(起点->①)方案数=Cx1−1x1+y1−2;
再求解(起点->②)=先忽略①存在求得的(起点->②)-(起点-> ①)*(①->②)=Cx2−1x2+y2−2−①∗Cx2−x1x2−x1+y2−y1;
最后可得(起点->终点)=忽略①②求得的(起点->终点)-(起点->①)∗(①->终点)-(起点->②)
如此类推即可.
代码实现
#include<bits/stdc++.h>
using namespace std;
#define maxn 2007
#define maxx 100007*2
typedef long long ll;
const ll mod=1e9+7;
ll ans[maxn],fac[maxx],inv[maxx];
struct node
{
int x;
int y;
}point[maxn];
bool cmp(node a,node b)
{
if(a.x==b.x)
return a.y<b.y;
return a.x<b.x;
}
ll quick_pow(ll a,ll b)
{
ll ans=1;
a%=mod;
while(b>0)
{
if(b%2)
ans=(ans*a)%mod;
a=(a*a)%mod;
b/=2;
}
return ans;
}
void init()
{
fac[0]=1;
inv[0]=1;
for(int i=1;i<maxx;i++)
{
fac[i]=(fac[i-1]*i)%mod;
inv[i]=quick_pow(fac[i],mod-2);
}
}
int main()
{
ios::sync_with_stdio(false);
int h,w,n;
cin>>h>>w>>n;
for(int i=0;i<n;i++)
cin>>point[i].x>>point[i].y;
point[n].x=h,point[n].y=w;
sort(point,point+n+1,cmp);
init();
for(int i=0;i<=n;i++)
{
int tx1=point[i].x,ty1=point[i].y;
ans[i]=fac[tx1+ty1-2]*inv[tx1-1]%mod*inv[ty1-1]%mod;
for(int j=0;j<i;j++)
{
int tx2=point[j].x,ty2=point[j].y;
if(tx2>tx1||ty2>ty1) continue;
ans[i]=ans[i]-ans[j]*(fac[ty1-ty2+tx1-tx2]*inv[tx1-tx2]%mod*inv[ty1-ty2]%mod)%mod;
ans[i]=(ans[i]%mod+mod)%mod;
}
}
cout<<ans[n]<<endl;
return 0;
}