HDU - 5698 瞬间移动 题解*

题面:

有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案,答案对1000000007取模。
在这里插入图片描述

输入:

多组测试数据。
两个整数n,m(2≤n,m≤100000)

输出:

一个整数表示答案

题目分析:

这个题目,初始是在左上角(1,1)的位置,最终要去(n,m)的位置,然后又只能往右下角走.所以这两个位置是钦定要走的.
所以相当于是在一个(n-2)(m-2)的地图往右下走,问有多少走走法
这时能走的步数最多为min(n-2,m-2),最少可以直接到达终点,一步都不在这个范围里面停留(指0步.
如果走0步,那么只有一种走法.
如果走1步,那么就有(n-2)
(m-2)种走法.
如果走2步,那么就有C(2, m-2)*C(2, n-2)种
.
.
.
所以用一个循环,那这个每一种步数的所有方法数加起来.
求组合数(比如C(n,m))时,用到公式:
C(n,m) = m! / (n! * (n-m)!)

这里除法的时候,要使用逆元将除法转换成乘法(乘以一个数的逆元=除以一个数)

这里要用到费马小定理来求逆元,当p是素数时
a(p-1) = 1 (mod p)
1/a = a(p-2)

然后用快速幂求出a(p-2)
这样就能求出n!和(n-m)!的逆元

在预处理时先把1-100000的阶乘和逆元都算出来
然后再计算时可以直接
fac[m]inv[n]%MODinv[m-n]%MOD

然后就是答案了.

代码:

#include<stdio.h>
#define MOD 1000000007
long n, m, min;
long long ans;
long long jc[100100], ni[100100];

long long mod_pow(long long x, long long n){
    long long res = 1;
    while(n > 0){
        if(n & 1) res = res * x % MOD;
        x = x * x % MOD;
        n = n >> 1;
    }
    return res;
}

void init(){
    jc[0] = 1;
    for(int i = 1; i <= 100001; ++i){
        jc[i] = jc[i-1]*i % MOD;
    }
    ni[0] = 1;
    ni[100001] = mod_pow(jc[100001], MOD-2);
    for(int i = 100000; i > 0; --i){
        ni[i] = ni[i+1] * (i+1) % MOD;
    }
}

long long c(long n, long m){
    return jc[m] * ni[n] % MOD * ni[m-n] % MOD;
}

int main(){
    init();
    while(scanf("%ld%ld", &n, &m) != EOF){
        n -= 2;
        m -= 2;
        min = (n>m)?m:n;
        ans = 1;
        for(int i = 1; i <= min; ++i){
            ans = (ans + c(i,n) % MOD * c(i,m) % MOD) % MOD;
        }
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值