【51nod】--机器人走方格V2(组合数学&&逆元&&费马小定理)

题目链接这里呀
思路来源于这两骗博客哦


1119 机器人走方格 V2
基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注
M * N的方格,一个机器人从左上走到右下,只能向右或向下走。有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果。
Input
第1行,2个数M,N,中间用空格隔开。(2 <= m,n <= 1000000)
Output
输出走法的数量 Mod 10^9 + 7。
Input示例
2 3
Output示例
3
思路:
哇!这道题涉及到了太多,太神奇了哦。
m*n的方格,从左上走到右下共需走(n+m-2)步,其中(n-1)步向右走,(m-1)步向下走。所以问从左上到右下共有多少种方法,也就是问向右走的(n-1)步有多少种方法或向下走的(m-1)步有多少种方法。问题转化为从(n+m-2)中选出(n-1)或(m-1)有多少种,即组合问题,C(n+m-2,m-1)或C(n+m-2,n-1),两个结果一样,所以取min(m-1,n-1)即可。
令n=n+m-2,m=min(n-1,m-1);结果为求C(n,m)%MOD;
结果形式为(a/b)%MOD;即除法取模,需要转换为乘法取模。有两种方法哦!
法一:
求乘法逆元,(a/b)%MOD=(a*b1)%MOD,b1为b%MOD的乘法逆元,求乘法逆元用扩展欧几里得算法即可。
注意:a,b很大,因为b=1(%MOD),那么b%MOD=1(%MOD),所以上面可认为b1是b%MOD的逆元,而且((a%MOD)/(b%MOD))%MOD=((a%MOD)*b1)%MOD;
所以思路是先用扩展欧几里得求出逆元b1,再最终求出((a%MOD)*b1)%MOD;
代码如下:

#include<cstdio>//扩展欧几里得求乘法逆元 
#include<algorithm>
#define MOD 1000000007
typedef long long LL;
using namespace std;
LL ex_gcd(LL a,LL b,LL &x,LL &y)        //扩展欧几里得 
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    LL r=ex_gcd(b,a%b,x,y);
    LL t=x;
    x=y;
    y=t-a/b*y;
    return r;
}
LL mod_reverse(LL a,LL n)//ax=1(mod n) 求乘法逆元 
{
    LL d,x,y;
    d=ex_gcd(a,n,x,y);
    if(d==1)
        return (x%n+n)%n;
    else
        return -1;
} 
LL C(LL m,LL n)//求组合数 
{
    LL ans1=1,ans2=1,i;
    for(i=n;i>=n-m+1;i--)//分子 
        ans1=ans1*i%MOD;
    for(i=1;i<=m;i++)//分母 
        ans2=ans2*i%MOD;
    return ans1*mod_reverse(ans2,MOD)%MOD;          //除法取模变乘法取模 
}
int main()
{
    LL m,n;
    while(~scanf("%lld%lld",&m,&n))
    {
        LL cnt=C(min(m-1,n-1),n+m-2);
        printf("%lld\n",cnt);
    } 
    return 0;
}

法二:
费马小定理:p是质数,a^p=a(%p),若a不是p的倍数,即a与p互质,则a^(p-1)=1(%p),
即b^(MOD-1)=1(%MOD)(MOD是质数且与b互质) —————————— 1式 ,
本题要求x=(a/b)%MOD,即(a/b)=x(%MOD)————-2式;
联立1,2式得,
a/b*b^(MOD-1)=x(%MOD)(因为,a%c=x,b%c=y,则a*b%c=x*y)
即a*b^(MOD-2)=x(%MOD),所以x=a*b^(MOD-2)%MOD;快速幂求解哦。
代码如下:

#include<cstdio>
#include<algorithm>
#define MOD 1000000007
typedef long long LL;
using namespace std;
LL Quick_Mod(LL a,LL b)     //快速幂 
{
    LL sum=1;
    while(b)
    {
        if(b&1)
            sum=sum*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return (sum+MOD)%MOD;
} 
int main()
{
    LL m,n,t;
    while(~scanf("%lld%lld",&m,&n))
    {
        t=min(n-1,m-1);//这里注意一下得换个元哦,若直接赋值给了m,那么下面的m就不能用了 
        n=n+m-2;
        LL i,ans1=1,ans2=1;
        for(i=n;i>=n-t+1;i--)
            ans1=ans1*i%MOD;
        for(i=1;i<=t;i++)
            ans2=ans2*i%MOD;
        LL ant=ans1*Quick_Mod(ans2,MOD-2)%MOD;
        printf("%lld\n",ant); 
    }
    return 0; 
}

认真理解,仔细分析哦!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值