除法逆元取模(费马小定理 | | 卢卡斯定理)

一直对逆元的知识半知半解。。。

逆元定义
若在 mod p 意义下,对于一个整数 a ,有 a*x=1(mod p);
那么这个这个整数x即为a的乘法逆元。a也为x的乘法逆元。

充分必要条件:gcd(a,p)=1mod p;即a p互质。

用到的知识点:
费马小定理
假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

通常情况下,p均为质数,则公约数为1的情况基本都可以保证

由费马小定理得:

b^(p-1)%p=1 则:

b*b^(p-2)%p=1 两边同乘a/b,然后左右式交换得:

a/b=a/bbb^(p-2)%p 化简得:

a/b=a*b^(p-2)%p

此时的结果即为a/b的结果,取模得(a/b)%p;

ll mod; //宏定义一个mod
 
//快速幂
ll quick_pow(ll a,ll b)
{
    ll ans=1;
    while(b){
        if(b&1) ans=(ans*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ans;
}
 
//逆元函数 公式为 (a/b)%mod=(a*b^(mod-2))%mod
ll inv(ll a,ll b)
{
    return (a*quick_pow(b,mod-2))%mod;
}
 
/***
注意事项:逆元函数的使用,a必须能整除b,并且mod为质数
**/
 
int main()
{
    ll a,b;
    while(cin>>a>>b>>mod){
        cout<<inv(a,b)<<endl;
    }
    return 0;
}

Lucas定理是用来求 c(n,m) mod p,p为素数的值。

卢卡斯定理的定义

假设我们要求的组合数为 C ( n , m ) ,因为这个数比较大,所以通常求的是 C ( n , m ) mod p。卢卡斯定理本来的定义是:

将 m 和 n 分别在 p 进制下表示出来:

在这里插入图片描述
那么一定有:
在这里插入图片描述
在这里插入图片描述
简单点就是:

C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p

代码:

// 需要先预处理出fact[],即阶乘
inline ll C(ll m, ll n, ll p)
{
    return m < n ? 0 : fact[m] * inv(fact[n], p) % p * inv(fact[m - n], p) % p;
}
inline ll lucas(ll m, ll n, ll p)
{
    return n == 0 ? 1 % p : lucas(m / p, n / p, p) * C(m % p, n % p, p) % p;
}
LL Lucas(LL n, LL m, int p){
         return m ? Lucas(n/p, m/p, p) * comb(n%p, m%p, p) % p : 1;
 }
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9+7;
//组合数取模模板(p较大时,如p=1e9+7) 
ll Pow(ll a,ll b)
{
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%p;
		a=(a*a)%p;
		b>>=1;
	}
	return ans;
}
 
ll C(ll n,ll m)
{
	if(m==0) return 1;
	if(m>n-m) m=n-m;
	ll up=1,down=1;
	for(int i=1;i<=m;i++){
		up=(up*(n-i+1))%p;
		down=(down*i)%p;
	}
	return up*Pow(down,p-2)%p;
}
 
/***
目的:输出C(n,m)%mod的值
时间复杂度:O(m)
**/
 
int main()
{
    ll m,n;
    while(scanf("%lld%lld",&n,&m)!=EOF){
    	printf("%lld\n",C(n,m));
	}
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =1e5;
ll n, m, p, fac[N];
void init()
{
    int i;
    fac[0] =1;
    for(i =1; i <= p; i++)
        fac[i] = fac[i-1]*i % p;
}
ll q_pow(ll a, ll b)
{
    ll  ans =1;
    while(b)
    {
        if(b &1)  ans = ans * a % p;
        b>>=1;
        a = a*a % p;   
    }
    return  ans;
}
 
ll C(ll n, ll m)
{
    if(m > n)  return 0;
    return  fac[n]*q_pow(fac[m]*fac[n-m], p-2) % p;
}
 
ll Lucas(ll n, ll m )
{
    if(m ==0)  return 1;
    else return  (C(n%p, m%p)*Lucas(n/p, m/p))%p;
}
 
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%I64d%I64d%I64d", &n, &m, &p);
        init();
        printf("%I64d\n", Lucas(n, m));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值