2017山东省第八届ACM省赛 D. HEX(组合数学)

题意:从(1,1)到(x,y)有多少种走法?每次可以向下,左下,右下走。

思路:可以算出从(1,1)到(x,y)最多走tot= x-1步,最多向左下走l = x-y步,向右下走r = tot - l步,向下走一步相当于走1步左下,1步右下。则向下走的步数最多为m = min(l,r)步。

枚举向下走的步数i,则向左下走l-i步,向右下走r - i步。那么不同的走法就是向下走,向左下和向右下走的全排列,即 (i + l - i + r - i)!/ (i! * (l - i)! *(r - i)!) % mod,除法取模相当于乘以逆元。

 

#include <cstdio>
#include <algorithm>
#include <iostream>
#include<vector>
#include<cmath>
#include<set>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const int maxt = 100200;
const int inf = 0x3f3f3f3f;
const ll INF = 0x7f7f7f7f7f;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
const double eps = 1e-8;
ll fact[100010]; // 阶乘
ll f[100010]; // 阶乘的逆
int cal(int l, int r, int m){ //计算全排列
    ll ans = fact[l + r + m] * f[l] % mod * f[r] % mod * f[m] % mod;
    return ans;
}
ll quick_mod(ll x, int n){ // 快速幂求逆元
    ll ret = 1;
    while(n){
        if(n & 1) ret = ret * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return ret;
}
void init(){
    fact[0] = 1;
    for(int i = 1; i <= 100000; ++i)
        fact[i] = fact[i - 1] * i % mod;
    f[0] = 1;
    f[100000] = quick_mod(fact[100000], mod - 2);
    for(int i = 99999; i >= 0; --i)
        f[i] = f[i + 1] * (i + 1) % mod;
}
int main(){
    int x, y;
    init();
    while(scanf("%d%d", &x, &y) == 2){
         int tot = x - 1; // 总步数
         int l = x - y, r = tot - l; //左下、右下的步数
         int minn = min(l, r); // 向下的步数
         ll ans = 0;
         for(int i = 0; i <= minn; ++i){
             int ll = l - i, rr = r - i;
             ans = (ans + cal(ll, rr, i)) % mod;
         }
         printf("%lld\n", ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值