首先是我自己的一些理解:乘法逆元类比倒数,若x乘上y在模p的意义下为1,则①y为x在模p的意义下的乘法逆元,记作xy≡1(mod p),其中②y%p为x的乘法逆元(划线的是乘法逆元的两种表示)。
ps:只有两个数(x和p)互质才有乘法逆元;对于上面的式子,当p为素数时,x的乘法逆元唯一。
然后是关于乘法逆元存在的意义:
• (a + b)%m = (a%m + b%m)%m
• (a − b)%m = (a%m − b%m)%m
• (a × b)%m = (a%m) × (b%m)%m
首先上面的三个式子是关于取模的一些运算法则,其中缺少除法运算,这是因为按照我们的逻辑来进行取模除法运算是不可行的(
例如3/4%5),所以定义了逆元,通过这种运算就可以算出3/4%5的值为2,即2为3在模5意义下的逆元。
再解释一些求乘法逆元的方法:第一种就是扩展欧几里得,条件为两数互质,单次复杂度大约是log级别的吧,注意扩展欧几里得求出的解中很可能有负数,若题目说求最小正整数解(参照luogu同余方程的题面),那么可以用(x+膜数)%膜数来转化(记住即可,证明比较麻烦);
第二种是费马小定理,这个要求模数p为素数才可以,还需要配合快速幂使用,单次复杂度同样是log级别;
第三种是一种线性的方法,条件是模数p为素数,且p必须要大于另一个数。
代码会陆续贴上。
第一种(类模板(需要一点的转化https://www.luogu.org/problemnew/solution/P1082):
#include<iostream>
using namespace std;
int a, b;
void exgcd(int a, int b, int &x, int &y) {
if (a == 0) {
x = 0, y = 1;
return ;
}
exgcd(b%a, a, y, x);
x -= b/a*y;
return ;
}
int main() {
int x, y;
cin >> a >> b;
exgcd(a, b, x, y);
cout << (x+b)%b;
return 0;
}
//b > a
第三种:
inv[1] = 1;
for (i = 2; i < p; i++)
inv[i] = (p-p/i)*inv[p%i]%p;
//在p为质数的情况下可以求出[1,p)所有数的逆元
另外,可以利用上面的方法实现O(n)预处理,O(1)查询组合数取模,博客地址: