Codeforces 559C:Gerald and Giant Chess
题目链接:Codeforces http://codeforces.com/problemset/problem/559/C
51nod http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1486
题目大意:有一个$h \times w$的棋盘,里面有一些格子是不能走的,只能每次向右边或者下面移动,现在要求从左上角走到右下角的方案数。
组合数+容斥原理
将不能走的格子的坐标按横坐标为第一关键字、纵坐标为第二关键字升序排序(按纵坐标为第一关键字、横坐标为第二关键字升序排序也可),用$dp[i]$维护从左上角移动到当前不能走的格子的方案数,根据组合数的性质及容斥原理可以得到:$dp[i]=C_{p[i].x}^{p[i].x+p[i].y}-\sum_{k=0}^{i-1}(dp[k] \times C_{p[i].x-p[k].x}^{p[i].x+p[i].y-p[k].x-p[k].y})$.
故从左上角走到右下角的方案数$ans=C_{h}^{h+w}-\sum_{i=0}^n(dp[i] \times C_{h-p[i].x}^{h+w-p[i].x-p[i].y})$.
代码如下:
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #define N 200005 5 #define M 2005 6 using namespace std; 7 typedef long long ll; 8 struct P{ 9 int x,y; 10 friend bool operator < (P a,P b){ 11 if(a.x==b.x)return a.y<b.y; 12 return a.x<b.x; 13 } 14 }p[M]; 15 ll dp[M],fac[N],inv[N],ans,m=1000000007; 16 int h,w,n; 17 ll Minus(ll a, ll b){return (a-b+m)%m;} 18 ll Mul(ll a, ll b){return (a*b)%m;} 19 ll pow_mod(ll a, ll n){ 20 ll r=1; 21 while(n){ 22 if(n&1)r=Mul(r,a); 23 a=Mul(a,a); 24 n>>=1; 25 } 26 return r; 27 } 28 ll Inv(ll a){return pow_mod(a,m-2);} 29 void init(){ 30 fac[0]=inv[0]=1; 31 for(int i=1;i<=200000;++i){ 32 fac[i]=Mul(fac[i-1],i); 33 inv[i]=Inv(fac[i]); 34 } 35 } 36 ll C(ll x,ll y){return Mul(Mul(fac[x],inv[x-y]),inv[y]);} 37 int main(void){ 38 init(); 39 scanf("%d%d%d",&h,&w,&n); 40 h--;w--; 41 for(int i=0;i<n;++i){ 42 scanf("%d%d",&p[i].x,&p[i].y); 43 p[i].x--;p[i].y--; 44 } 45 sort(p,p+n); 46 ans=C(h+w,h); 47 for(int i=0;i<n;++i){ 48 dp[i]=C(p[i].x+p[i].y,p[i].x); 49 for(int j=0;j<i;++j) 50 if(p[i].x>=p[j].x&&p[i].y>=p[j].y) 51 dp[i]=Minus(dp[i],Mul(dp[j],C(p[i].x+p[i].y-p[j].x-p[j].y,p[i].x-p[j].x))); 52 ans=Minus(ans,Mul(dp[i],C(h+w-p[i].x-p[i].y,h-p[i].x))); 53 } 54 printf("%lld\n",ans); 55 }