1. 程式人生 > >51NOD-1486 大大走格子

51NOD-1486 大大走格子

OS -s font void 目的 local std struct 結果

有一個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取余的結果。

Sample Input

3 4 2
2 2
2 3

Sample Output

2

題目鏈接

分析

從左上角(1,1)走到(x,y)的方案數為C(x-1+y-1,x-1)。設d[i]為到達第i個黑點且中間不經過任何黑點的方案數。則有d[i]=C(xi+yi-2,xi-1)-
d[j]*C(xi+yi-xj-yj,xi-yi)(xj<xi,yj<yi).然後令第n+1個黑點為(h,w),答案即為d[n+1]。為什麽這樣是正確的呢?
對於一個黑點,可能可以由另外的黑點到達。實際上枚舉時總是從第一個能經過的黑點出發,每個黑點會對目的黑點的值有影響,
貢獻為該黑點的值乘上從該黑點走到目的黑點的方案數。(因為只要經過黑點就是非法的,所以後面沒有限制,直接走就好了)
#include <iostream>
#include 
<cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <map> #include <vector> typedef long long LL; const int maxn = 2e5+5; const int inf = 0x3f3f3f3f; const int mod = 1000000007; using namespace std; LL fact[maxn],inv[maxn]; struct node{
int x,y; }p[2020]; LL _inv(int x){ if(x==1) return 1; return (mod-mod/x)*_inv(mod%x)%mod; } void init(){ fact[0]=1; for(int i=1;i<maxn;i++){ fact[i]=(fact[i-1]*i)%mod; } for(int i=0;i<maxn;i++){ inv[i]=_inv(fact[i]); } } LL C(int a,int b){ if(a<b) return 0; return ((fact[a]*inv[b])%mod*inv[a-b])%mod; } int cmp(node a,node b){ if(a.x==b.x) return a.y<b.y; return a.x<b.x; } LL d[2020]; int main(){ #ifdef LOCAL freopen("in.txt","r",stdin); #endif init(); int h,w,n; scanf("%d%d%d",&h,&w,&n); for(int i=0;i<n;i++) scanf("%d%d",&p[i].x,&p[i].y); sort(p,p+n,cmp); p[n].x=h,p[n].y=w; n++; for(int i=0;i<n;i++){ d[i]=C(p[i].x+p[i].y-2,p[i].x-1); for(int j=0;j<i;j++){ if(p[i].y>=p[j].y&&p[i].x>=p[j].x){ d[i] -= (d[j]*C(p[i].x+p[i].y-p[j].x-p[j].y,p[i].x-p[j].x))%mod; if(d[i]<0){ d[i]+=mod; } } } } cout<<d[n-1]; return 0; }

51NOD-1486 大大走格子