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

思路:可以算出从(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,除法取模相当于乘以逆元。

 

这是个组合数学题目,现在看来高中组合数学是白学了,一共有(i + l - i + r - i)多步,所以阶乘,去重的话,就是比如向左走一步然后向右走一步和向下走一步和向右走一向左走一是一样的,所以除以(i! * (l - i)! *(r - i)!)  比如只有向下走的时候不用排序,但是还是排序了,所以除以他们内部的排序。

#include <bits/stdc++.h>
using namespace std;
typedef  long long ll;
int mod=1e9+7;
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;
}

//LL a,b;
int main()
{
int x,y;
init();
while(~scanf("%d%d",&x,&y))
{
    int tot=x-1;
    int l=x-y;
    int r=tot-l;
    int minn=min(l,r);
    ll ans=0;
    for(int i=0;i<=minn;i++)
    {
        int ll=l-i;
        int rr=r-i;
        ans+=cal(ll, rr, i);
        ans%=mod;
    }
    printf("%lld\n",ans);
}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/clx55555/article/details/80085426