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.
3.0 KiB
3.0 KiB
思路
一、前置知识
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;
}