逆元定义
如果 a x ≡ 1 ( m o d p ) ax\equiv 1\pmod p ax≡1(modp),则称 x x x 为 a a a 在模 p p p 意义下的乘法逆元。
逆元存在当且仅当 a ⊥ p a\perp p a⊥p,即 gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1。
将 a x ≡ 1 ( m o d p ) ax\equiv 1\pmod p ax≡1(modp) 转化可得 x ≡ 1 a ( m o d p ) x\equiv\dfrac{1}{a}\pmod p x≡a1(modp),那么模意义下 t ÷ a t \div a t÷a 就相当于 t × x t \times x t×x。
快速求单个数的逆元
快速幂
费马小定理
当 p ∤ a p\not\mid a p∣a 且 p ∈ P p\in\mathbb{P} p∈P 时,有
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap−1≡1(modp)
由此转化可得 a × a p − 2 ≡ 1 ( m o d p ) a\times a^{p-2}\equiv 1\pmod p a×ap−2≡1(modp),此时就可以发现 a p − 2 a^{p-2} ap−2 即为 a a a 在模 p p p 意义下的乘法逆元。
此时可以用快速幂计算逆元,时间复杂度 O ( log p ) O(\log p) O(logp)。
扩展欧几里得算法
Exgcd \text{Exgcd} Exgcd 也可以用于求逆元。
由裴蜀定理可知,若 a ⊥ p a\perp p a⊥p,则必然 ∃ y \exist\, y ∃y,使得 a × x + p × y = 1 a\times x+p\times y=1 a×x+p×y=1 成立。
容易发现, a × x + p × y = 1 ⇔ a × x ≡ 1 ( m o d p ) a\times x+p\times y=1\Leftrightarrow a\times x\equiv 1\pmod p a×x+p×y=1⇔a×x≡1(modp)。
使用 exgcd \text{exgcd} exgcd 解出这个方程即可,时间复杂度 O ( log p ) O(\log p) O(logp)。
线性求 1 ∼ n 1\sim n 1∼n 的逆元
例题:loj #110. 乘法逆元。
现要求在 O ( n ) O(n) O(n) 的时间内,求出模 m m m 的意义下, [ 1 , m ) [1,m) [1,m) 中所有数的乘法逆元。
容易发现, ∀ m ∈ Z \forall\, m\in\mathbb{Z} ∀m∈Z,有 1 × 1 ≡ 1 ( m o d m ) 1 \times 1 \equiv 1\pmod m 1×1≡1(modm),所以 1 1 1 的逆元恒为 1 1 1。
考虑 递推。
假设我们已知 [ 1 , x ) [1,x) [1,x) 内所有数的逆元,需要求出 x x x 的逆元。
将模数 m m m 表示为 k x + t kx+t kx+t,其中 k = ⌊ m x ⌋ k=\Big\lfloor\dfrac{m}{x}\Big\rfloor k=⌊xm⌋, t = m m o d x t=m\bmod x t=mmodx。
由此可得 k x + t ≡ 0 ( m o d m ) kx+t\equiv 0\pmod m kx+t≡0(modm)。
两边同乘 x − 1 × t − 1 ( m o d m ) x^{-1}\times t^{-1}\pmod m x−1×t−1(modm),可得
k x × x − 1 × t − 1 + t × x − 1 × t − 1 ≡ 0 ( m o d m ) k × t − 1 + x − 1 ≡ 0 ( m o d m ) \begin{aligned} kx\times x^{-1}\times t^{-1}+t\times x^{-1}\times t^{-1}\equiv 0\pmod m\\ k\times t^{-1}+x^{-1}\equiv 0\pmod m\\ \end{aligned} kx×x−1×t−1+t×x−1×t−1≡0(modm)k×t−1+x−1≡0(modm)
即可得 x − 1 ≡ k × t − 1 ( m o d m ) x^{-1}\equiv k\times t^{-1}\pmod m x−1≡k×t−1(modm)。
在此基础上代入
k
=
⌊
m
x
⌋
k=\Big\lfloor\dfrac{m}{x}\Big\rfloor
k=⌊xm⌋,
t
=
m
m
o
d
x
t=m\bmod x
t=mmodx,可得
x
−
1
≡
−
⌊
m
x
⌋
(
m
m
o
d
x
)
−
1
(
m
o
d
m
)
x^{-1} \equiv -\Big\lfloor\dfrac{m}{x}\Big\rfloor(m\bmod x)^{-1}\pmod m
x−1≡−⌊xm⌋(mmodx)−1(modm)
由于
(
m
m
o
d
x
)
−
1
∈
[
1
,
x
)
(m\bmod x)^{-1}\in[1,x)
(mmodx)−1∈[1,x),所以
(
m
m
o
d
x
)
−
1
(m\bmod x)^{-1}
(mmodx)−1 已知,线性递推即可。
- PS 1 \textbf{PS 1} PS 1:当 m m o d x = 0 m\bmod x=0 mmodx=0 时 x x x 不存在模 m m m 意义下的乘法逆元。
- PS 2 \textbf{PS 2} PS 2:由于 C++ \text{C++} C++ 中负数取模的结果为负数,所以在实际实现时递推式应写为
(m - m / x) * inv[m % x] % m
。
综上所述,递推式即为:
x
−
1
=
{
1
x
=
1
−
⌊
m
x
⌋
(
m
m
o
d
x
)
−
1
x
≠
1
(
m
o
d
m
)
x^{-1}= \begin{cases} 1&x=1\\ -\Big\lfloor\dfrac{m}{x}\Big\rfloor(m\bmod x)^{-1}&x\not=1 \end{cases} \pmod m
x−1={1−⌊xm⌋(mmodx)−1x=1x=1(modm)
线性递推即可在
O
(
n
)
O(n)
O(n) 的时间内完成。
线性求任意 n n n 个数的逆元
例题:loj #161 乘法逆元 2。
显然,对于每一个数用 快速幂 / exgcd \text{exgcd} exgcd 求解,时间复杂度为 O ( n log p ) O(n\log p) O(nlogp),无法通过本题。
考虑使用前缀积 { s n } \{s_n\} {sn},其中 s i s_i si 记录 ∏ j = 1 i a j \prod_{j=1}^{i}a_j ∏j=1iaj。
此外需要前缀积的逆元,使用 { t n } \{t_n\} {tn} 序列,其中 t i = s i − 1 ( m o d p ) t_i=s_{i}^{-1}\pmod p ti=si−1(modp)。
接下来可以通过 快速幂 / exgcd \text{exgcd} exgcd 求解 s n s_n sn 的逆元,记为 t n t_n tn。
接下来可以通过如下公式线性递推求出
{
t
n
}
\{t_n\}
{tn}:
t
i
≡
s
i
−
1
≡
1
∏
j
=
1
i
a
j
≡
a
i
+
1
∏
j
=
1
i
+
1
a
j
≡
a
i
+
1
×
s
i
+
1
−
1
≡
a
i
+
1
×
t
i
+
1
(
m
o
d
p
)
t_i \equiv s_i^{-1} \equiv \dfrac{1}{\prod_{j=1}^{i}a_j} \equiv \dfrac{a_{i+1}}{\prod_{j=1}^{i+1}{a_j}} \equiv a_{i+1}\times s_{i+1}^{-1} \equiv a_{i+1}\times t_{i+1}\pmod p
ti≡si−1≡∏j=1iaj1≡∏j=1i+1ajai+1≡ai+1×si+1−1≡ai+1×ti+1(modp)
记原序列的逆元为
{
inv
n
}
\{\text{inv}_n\}
{invn},其中
inv
i
=
a
i
−
1
(
m
o
d
p
)
\text{inv}_i=a_{i}^{-1}\pmod p
invi=ai−1(modp)。
由于已知前缀积数组的逆元,那么
inv
i
≡
a
i
−
1
≡
1
a
i
≡
1
s
i
s
i
−
1
≡
s
i
−
1
s
i
≡
s
i
−
1
×
t
i
\text{inv}_i \equiv a_{i}^{-1} \equiv \dfrac{1}{a_i} \equiv \dfrac{1}{\dfrac{s_i}{s_{i-1}}} \equiv \dfrac{s_{i-1}}{s_i} \equiv s_{i-1}\times t_{i}
invi≡ai−1≡ai1≡si−1si1≡sisi−1≡si−1×ti
故递推即可,时间复杂度为
O
(
n
+
log
p
)
O(n+\log p)
O(n+logp),其中
O
(
log
p
)
O(\log p)
O(logp) 是求
s
n
s_n
sn 逆元的复杂度。