51nod 1119 机器人走方格V2(求组合数,费马小定理+快速幂/逆元+gcd)

题目:点击打开链接

额,,,走方格不是走边。

杨辉三角形,从A出发到每个点的走法数等于它左边点和上边点的走法相加
1  1  1    1

1  2  3    4

1  3  6   10

1  4    10    20




可以看出结果为C(min(n-1,m-1),n+m-2 )     

令m+n-2=m, m-1=n, 即c(n, m)=m!/((n-m)!*n!)%N;  再令:a=m!/(m-n)!,b=n!;

所以只需要求 a / b%N;但a,b本身就超过long long了,为了防止溢出,可以(a%N )/ (b%N)%N,但我们发现除法没法这么算,

因为比如9 / 3%14,搞完3%14成0了,b%N经常会变成0,会出错。

所以可以这么处理:利用费马小定理

费马小定理:b^(N - 1)=1(mod N),前提是b是整数,N是质数,b,N互质,显然符合

而a / b=x(mod N),两边关于N互余

两边相乘,得,a * b^(N - 2)=x(mod N);用快速幂处理即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
const ll mod=1e9+7;
ll get_pow(ll x, ll n){  //快速幂
    ll ans=1;
    while(n){
        if(n&1){
            ans=ans*x%mod;
        }
        x=x*x%mod;
        n>>=1;
    }
    return (ans+mod)%mod;
}
ll C(ll n,ll m){  //C(n,m),m里选n个。
    ll ans,a=1,b=1;
    for(ll i=m;i>=m-n+1;i--)
        a=a*i%mod;
    for(ll i=n;i>1;i--)
        b=b*i%mod;
    ans=a*get_pow(b,mod-2)%mod;
    return ans;
}
int main(){
    ll n,m;
    cin>>n>>m;
    ll ans=C(min(n-1,m-1),m+n-2);  
    cout<<ans<<endl;
    return 0;
}

还有一种处理方法比较类似,求逆元。

可以转化为求a * b'%N,其中b’为%N的乘法逆元。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#define ll long long
using namespace std;
const ll mod=1e9+7;
void gcd(ll a,ll b,ll &d,ll &x,ll &y){
    if(!b){d=a;x=1;y=0;}
    else{ gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
ll inv(ll a,ll n){
    ll d,x,y;
    gcd(a,n,d,x,y);
    return d==1? (x+n)%n:-1;
}
ll C(ll n,ll m){  //C(n,m),m里选n个。
    ll ans,a=1,b=1;
    for(ll i=m;i>=m-n+1;i--)
        a=a*i%mod;
    for(ll i=n;i>1;i--)
        b=b*i%mod;
    ans=a*inv(b,mod)%mod;
    return ans;
}
int main(){
    ll n,m;
    cin>>n>>m;
    ll ans=C(min(n-1,m-1),m+n-2);
    cout<<ans<<endl;
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值