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

3.0 KiB
Raw Blame History

思路

一、前置知识

1、什么是逆元

2、为什么要创造逆元的概念

二、乘法逆元

$A$除以一个数模$P$,等于$A$乘以这个数的乘法逆元模$P$。

(A/B)\% 9937 = (A*B^{-1}) \%9973. 这里的B^{-1} 代表乘法逆元,而不是\frac{1}{B}.

所以我们只需要求解出$B$对于$9973$的 乘法逆元$B^{-1}$ 即可。

三、解法一:费马小定理求解乘法逆元

只适用于模数为质数的情况

若$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}$。

// 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;
}

四、解法二:扩展欧几里得

根据 扩展欧几里得 : 模数可以 不为质数

因为$gcd(B,9973) = 1$,所以 Bx ≡ 1(mod \ 9973), x 是 $B^{-1}$,也就是$B$的乘法逆元 通过ex_gcd(B,9973)求出来的x就是B^{-1},因为求出来的$x$可能是负数,利用通解所以处理一下

x = (x % t + t) % t;

实现代码

// 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;
}