Codeforces 559C:Gerald and Giant Chess/51nod 1486:大大走格子

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 }

 

转载于:https://www.cnblogs.com/barrier/p/6739884.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值