最近学会了乘法逆元,有做了几道题,才发现乘法逆元在有除法又要取模的题目中非常有用。
乘法逆元
1.定义
若整数
b
,
m
b,m
b,m互质,并且
b
∣
a
b|a
b∣a,则存在一个整数
x
x
x,使得
a
/
b
≡
a
∗
x
(
m
o
d
m
)
a/b \equiv a*x \pmod{m}
a/b≡a∗x(modm)。称
x
x
x为
b
b
b的模m乘法逆元,记为
b
−
1
(
m
o
d
m
)
b^{-1}\pmod{m}
b−1(modm)。
2.证明
因为
a
/
b
≡
a
∗
b
−
1
≡
a
/
b
∗
b
∗
b
−
1
(
m
o
d
m
)
a/b \equiv a*b^{-1} \equiv a/b*b*b^{-1} \pmod{m}
a/b≡a∗b−1≡a/b∗b∗b−1(modm),所以
b
∗
b
−
1
≡
1
(
m
o
d
m
)
b*b^{-1} \equiv 1 \pmod{m}
b∗b−1≡1(modm)。
如果
m
m
m是质数(此时我们用符号
p
p
p代替
m
m
m)并且
b
<
p
b<p
b<p,根据费马小定理,
b
p
−
1
≡
1
(
m
o
d
p
)
b^{p-1} \equiv 1 \pmod{p}
bp−1≡1(modp),即
b
∗
b
p
−
2
≡
1
(
m
o
d
p
)
b*b^{p-2} \equiv 1 \pmod{p}
b∗bp−2≡1(modp)。因此,当模数p为质数时,
b
p
−
2
b^{p-2}
bp−2即为
b
b
b的乘法逆元。
3.用处
有了乘法逆元,在遇到有
a
/
b
a/b
a/b的计数类问题中,只需要求出乘法元,然后直接
a
∗
x
a*x
a∗x即可。
乘法逆元的应用
给一道题
POJ1845
题目描述
【题意】
Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).
题目关于两个自然数A和B。S是所有A^B的自然数因子的和。输出S模以9901(就是S除以9901的余数)
【输入格式】
The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.
只有一行,两个用空格隔开的自然数A和B(0<=A,B<=50000000)
【输出格式】
The only line of the output will contain S modulo 9901.
只有一行,S模以9901
【样例输入】
2 3
【样例输出】
15
【提示】
2^3 = 8.
The natural divisors of 8 are: 1,2,4,8. Their sum is 15.
15 modulo 9901 is 15 (that should be output).
8的自然数因子有:1,2,4,8。它们的和是15。
15模以9901就是15。
我们直接分解A的质因数,表示为
A
=
p
1
c
1
∗
p
2
c
2
∗
.
.
.
∗
p
n
c
n
A=p_1^{c_1}*p_2^{c_2}*...*p_n^{c_n}
A=p1c1∗p2c2∗...∗pncn。所以
A
B
A^B
AB的所有约数之和为:
(
1
+
p
1
+
p
1
2
+
.
.
.
+
p
1
B
∗
c
1
)
∗
(
1
+
p
2
+
p
2
2
+
.
.
.
+
p
2
B
∗
c
2
)
∗
.
.
.
∗
(
1
+
p
3
+
p
3
2
+
.
.
.
+
p
3
B
∗
c
3
)
(1+p_1+p_1^2+...+p_1^{B*c_1})*(1+p_2+p_2^2+...+p_2^{B*c_2})*...*(1+p_3+p_3^2+...+p_3^{B*c_3})
(1+p1+p12+...+p1B∗c1)∗(1+p2+p22+...+p2B∗c2)∗...∗(1+p3+p32+...+p3B∗c3)。
上式的每一项都是一个等比数列。以第一项为例,
(
1
+
p
1
+
p
1
2
+
.
.
.
+
p
1
B
∗
c
1
)
=
(
p
1
B
∗
c
1
+
1
−
1
)
/
(
p
1
−
1
)
(1+p_1+p_1^2+...+p_1^{B*c_1})=(p_1^{B*c_1+1}-1)/(p_1-1)
(1+p1+p12+...+p1B∗c1)=(p1B∗c1+1−1)/(p1−1)。
发一波等比数列求和的推导公式
所以我们可以先用快速幂计算分子
(
p
1
B
∗
c
1
+
1
−
1
)
m
o
d
9901
(p_1^{B*c_1+1}-1) \mod 9901
(p1B∗c1+1−1)mod9901 和分母
(
p
1
−
1
)
m
o
d
9901
(p_1-1)\mod9901
(p1−1)mod9901。因为9901时质数,只要
p
1
−
1
p_1-1
p1−1不是9901的倍数,就只需要计算
p
1
−
1
p_1-1
p1−1的乘法逆元
i
n
v
inv
inv,用乘
i
n
f
inf
inf代替除以
(
p
1
−
1
)
(p_1-1)
(p1−1),直接计算出等比数列求和公式的结果。
特别的,若
p
1
−
1
p_1-1
p1−1是9901的倍数,此时乘法逆元不存在,但是
p
1
m
o
d
9901
=
1
p_1\mod 9901=1
p1mod9901=1,所以
1
+
p
1
+
p
1
2
+
.
.
.
+
p
1
B
∗
c
1
≡
1
+
1
+
1
2
+
.
.
.
+
1
B
∗
c
1
(
m
o
d
9901
)
1+p_1+p_1^2+...+p_1^{B*c_1} \equiv 1+1+1^2+...+1^{B*c_1} \pmod{9901}
1+p1+p12+...+p1B∗c1≡1+1+12+...+1B∗c1(mod9901)。
参考代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a, b, m, ans = 1, mod = 9901;
int p[20], c[20];
void divide(int n) {
m = 0;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
p[++m] = i, c[m] = 0;
while (n % i == 0) n /= i, c[m]++;
}
}
if (n > 1)
p[++m] = n, c[m] = 1;
}
int power(int a, long long b) {
int c = 1;
for (; b; b >>= 1) {
if (b & 1) c = (long long)c * a % mod;
a = (long long) a * a % mod;
}
return c;
}
int main() {
cin >> a >> b;
divide(a); // 分解质因数
for (int i = 1; i <= m; i++) {
if ((p[i] - 1) % mod == 0) { // 没有逆元时,特判
ans = ((long long)b * c[i] + 1) % mod * ans % mod;
continue;
}
int x = power(p[i], (long long)b * c[i] + 1); // 分子
x = (x - 1 + mod) % mod;
int y = p[i] - 1; // 分母
y = power(y, mod - 2); // 根据费马小定理求乘法逆元
ans = (long long)ans * x % mod * y % mod;
}
cout << ans <<endl;
}