Files
python/TangDou/AcWing/NumberTheory/逆元/费马小定理+扩展欧几里得求逆元.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

123 lines
3.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 思路
### 一、前置知识
#### 1、什么是逆元
#### 2、为什么要创造逆元的概念
### 二、乘法逆元
$A$除以一个数模$P$,等于$A$乘以这个数的乘法逆元模$P$。
$(A/B)\% 9937 = (A*B^{-1}) \%9973$. 这里的$B^{-1}$ 代表乘法逆元,而不是$\frac{1}{B}$.
所以我们只需要求解出$B$对于$9973$的 <font color='red'><b>乘法逆元$B^{-1}$</b></font> 即可。
### 三、解法一:费马小定理求解乘法逆元
<font color='red'><b>只适用于模数为质数的情况</b></font>
若$a$是一个整数(除法分母),$b$是一个质数(模),那么 $a^b ≡ a(mod \ b)$ 或 $a^{b-1}≡1 (mod\ b)$
由扩展欧几里得可以得出:
因为$gcd(a,b) = 1, ax ≡ 1(mod \ b)$ $x$是**乘法逆元**
$a^{b-1} = a*a^{b-2}= ax ≡1(mod \ b)$. 所以 $x = a^{b-2}$就是乘法逆元。 可以通过快速幂求解。
在本题求$x = a^{9973-2}$。
```c++
// https://vjudge.csgrandeur.cn/problem/HDU-1576
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/*
测试用例
2
1000 53
87 123456789
答案
7922
6060
总结:
1、如果使用费马小定理+快速幂求逆元,一定要注意使用LL,防止爆int。
*/
const int MOD = 9973;
LL qmi(LL a, LL k, LL p) {
LL res = 1;
while (k) {
if (k & 1) res = res * a % p; //如果res是int,可能会越界
k >>= 1;
a = a * a % p; //如果a是int, 可能会越界
}
return res;
}
int main() {
int T;
cin >> T;
while (T--) {
LL a, b;
cin >> a >> b;
LL x;
x = qmi(b, MOD - 2, MOD); // b理解为分母,同余MOD的逆元,变除法为乘法
LL res = a * x % MOD; // a* b在模MOD下的逆元 x
printf("%lld\n", res);
}
return 0;
}
```
### 四、解法二:扩展欧几里得
<font color='red'><b>根据 扩展欧几里得 : 模数可以 不为质数</b></font>
因为$gcd(B,9973) = 1$,所以 $Bx ≡ 1(mod \ 9973)$, $x$ 是 $B^{-1}$,也就是$B$的乘法逆元
通过`ex_gcd(B,9973)`求出来的`x`就是$B^{-1}$,因为求出来的$x$可能是负数,利用通解所以处理一下
```C++
x = (x % t + t) % t;
```
实现代码
```c++
// https://vjudge.csgrandeur.cn/problem/HDU-1576
#include <bits/stdc++.h>
typedef long long LL;
/*
测试用例
2
1000 53
87 123456789
答案
7922
6060
*/
using namespace std;
const int MOD = 9973;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int t;
cin >> t;
while (t--) {
LL x, y;
LL a, b;
cin >> a >> b;
exgcd(b, MOD, x, y);
x = (x % MOD + MOD) % MOD; //对比使用费马小定理,这里对于扩展欧几里得算出的逆元,还需要进一步模一下 MOD才行,否则可能是负的
cout << (x * a) % MOD << endl;
}
return 0;
}
```