逆元(关于除法取模)

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

**逆元:**对于正整数 a 和 p,若 a * x % p ≡ 1(即 ax ≡ 1(mod p)),则称 x 为 a % p 的逆元,即把这个同余方程中 x 的最小正整数解叫做 a 模 p 的逆元。
一个数有逆元的充分必要条件是 gcd(a, p) = 1,此时逆元唯一存在。

除法取模
a/b mod m 等价于 a*b’ mod m (b’ 是 b 模 m 的逆元)

下面是几种求逆元的方法

1、扩展欧几里得

#include<stdio.h>
 #define ll long long
  ll exgcd(ll b,ll m,ll& x,ll& y){
      if(!m){
          x=1;
          y=0;
          return x;
     }
     else{
         exgcd(m,b%m,y,x);
         y-=x*(b/m);
     }
     return x;
 }

 int main(){
     ll b,m,x,y;
     while(scanf("%lld%lld",&b,&m)!=EOF){ //b有逆元的前提gcd(b,m)=1
         x=exgcd(b,m,x,y);
         x=(x%m+m)%m;
         printf("%lld\n",x);//x即为b的逆元b'
     }
     return 0;
 }

2、费马小定理

在模 m 为素数的条件下有 b(m-1) = 1 (mod m);
则 b 模 m 的逆元 b’ = b(m-2)
用快速幂求即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
#define LL long long  
using namespace std;

LL b,m;
LL pow(LL x,LL n){  
    LL res=1;
    while(n>0){
        if(n&1)
        res=(res*x);
        x=(x*x);
        n>>=1;
    }
    return res;
}

int main(){
    while(cin>>b>>m){
    	cout<<pow(b,m-2)%m<<endl; 
    }
    return 0;
}

3、求逆元的一般公式

a/b mod m = a mod (m*b) /b

注意:实际上a mod (m*b)/b这种的对于所有的都适用,不区分互不互素,而费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素,如果 a 与 m 不互素,那就没有逆元,这个时候需要 a mod (bm)/b 来搞(此时就不是逆元的概念了)。但是当 a 与 m 互素的时候,bm 可能会很大,不适合套这个一般公式,所以大部分时候还是用逆元来搞.