You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
python/TangDou/AcWing/NumberTheory/逆元/费马小定理+扩展欧几里得求逆元.md

3.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

思路

一、前置知识

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), xB^{-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;
}