51nod 1486 大大走格子

题目链接:51nod 1486

 

sol:

考虑没有障碍的情况,从(1,1)到(n,m)答案可直接算出为\binom{n-1+m-1}{n-1}。考虑经过不合法点(x,y)到达(n,m)的路径,答案为\binom{x-1+y-1}{x-1}*\binom{n-x+m-y}{m-y}。按照x为第一关键字,y为第二关键字对不合法点进行排序,设dp[i]为不经过其余不合法点从起点走到第i个点的方案数,则只有左上方的点对其方案有影响,依次删掉即可。可自行添加最后一个点处理答案。

可得到递推式dp[i] = \binom{x[i]-1+y[i]-1}{x[i]-1}-\sum_{j=1}^{i-1}[ y[j]<=y[i]]\binom{x[i]-x[j]+y[i]-y[j]}{x[i]-x[j]}

 

code:

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <stdio.h>
#include <queue>
#include <vector>
#include <cmath>
#include <set>
#include <sstream>

using namespace std;
typedef long long ll;
const int maxn = 3e3 + 50;
const ll mod = 1e9 + 7;

ll fac[maxn];
ll inv[maxn];

inline ll qpow(ll a, ll b){
    ll sum = 1;
    while (b) {
        if (b & 1) sum = sum * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return sum;
}

inline ll Lnv(int n){
    return qpow(n, mod - 2);
}

void init(int n){
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; i++) {
        fac[i] = fac[i - 1] * i % mod;
    }
    inv[n] = Lnv(fac[n]);
    for (int i = n - 1; i > 1; i--) {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }
}

inline ll C(int n, int m){
    return fac[n] * inv[n - m] % mod * inv[m] % mod;
}

struct node{
    int x,y;
    bool operator <(const node& s) const{
        return x==s.x?y<s.y:x<s.x;
    }
}q[maxn];

ll cal(int x,int y){
    return C(x+y,x);
}

ll dp[maxn];

int main(){
    int h,w,n;
    scanf("%d%d%d",&h,&w,&n);
    init(maxn-1);
    for(int i=1;i<=w;i++){
        scanf("%d%d",&q[i].x,&q[i].y);
    }
    ++n;
    q[n].x = h;
    q[n].y = w;
    sort(q+1,q+n+1);
    for(int i=1;i<=n;i++){
        // cout<<i<<' '<<q[i].x<<' '<<q[i].y<<endl;
        dp[i] = cal(q[i].x-1,q[i].y-1);
        for(int j=1;j<i;j++){
            if(q[j].y>q[i].y) continue;
            ll ret =  cal(q[i].x-q[j].x,q[i].y-q[j].y);
            (ret*=dp[j])%=mod; 
            // cout<<"ret "<<ret<<endl;
            (dp[i]+=mod-ret)%=mod;
        }
        // cout<<dp[i]<<endl;
    }
    printf("%lld\n",dp[n]);
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值