前言
针对比赛学的一点点逆元,在这里记录一下其中一种方法。
当然求逆元的方法有很多种,之后学到再回来写。 —— 2021.8.24
UPDATE
-
2021 - 11 - 25 填坑。
添加了两种求逆元的方法 + 修改了一些写得不好(
难看至极)的地方 -
2021 - 12 - 01 补充了欧拉定理的证明。
定义
若 a x ≡ 1 ( m o d b ) ax \equiv 1\pmod b ax≡1(modb) 且 a ⊥ b a\perp b a⊥b(a,b 互质),
则称 x x x 为 a a a 的逆元。
一、费马小定理 + 快速幂
若 p 为质数,且 q ⊥ p q \perp p q⊥p,根据费马小定理,有 q p − 1 ≡ 1 ( m o d p ) q^{p-1} \equiv 1 \pmod {p} qp−1≡1(modp),
带入原柿子中就有:
∵ a x ≡ 1 ≡ a p − 1 ( m o d p ) \because ax \equiv 1 \equiv a^{p-1} \pmod p ∵ax≡1≡ap−1(modp)
∴ x ≡ a p − 2 ( m o d p ) \therefore x \equiv a^{p-2} \pmod p ∴x≡ap−2(modp)
然后直接用快速幂求解即可。
代码
省略快速幂。
inline int fermat (int a, int n)
{
return pow_mod (a, n - 2, n);
}
补:费马小定理证明
费马小定理实际上就是 欧拉定理(证明) 的一个特殊情况。
我们定义 φ x \varphi_{x} φx 为从 1 到 x 和 x 互质的整数的数量。
例如: φ 8 = 4 \varphi_{8}=4 φ8=4,分别是:1、3、5、7。
欧拉定理就是: x φ p ≡ 1 ( m o d p ) x^{\varphi_p} \equiv 1 \pmod p xφp≡1(modp)。
而费马小定理就是当 p 为质数时的一个特殊情况 。
易证,当 p 为质数时, φ p = p − 1 \varphi_p=p-1 φp=p−1。
所以当 p 为质数时, x p − 1 ≡ 1 ( m o d p ) x^{p-1} \equiv 1 \pmod p xp−1≡1(modp)。
二、扩展欧几里得
∵ a x ≡ 1 ( m o d p ) ( x 为逆元 ) \because ax \equiv 1 \pmod p (\text{x 为逆元}) ∵ax≡1(modp)(x 为逆元)
∴ a x + b y = 1 \therefore ax + by=1 ∴ax+by=1
这样就可以直接用扩欧求逆元了。
但对于一次性求许多逆元(如洛谷的模板),效率并不高。
代码
省略扩欧代码。
inline int exgcd_inv (int a, int n)
{
int gcd, x, y;
gcd = exgcd (a, n);//求解方程 ax + ny = gcd (a, n)
return d == 1 ? (x + n) % n : -1;
}
三、线性求逆元
首先,有 1 − 1 ≡ 1 ( m o d p ) 1^{-1} \equiv 1\pmod p 1−1≡1(modp),
再设 p = k ∗ i + r , r < i , 1 < i < p p=k*i+r,\ r<i,\ 1<i<p p=k∗i+r, r<i, 1<i<p,
这样就有 k ∗ i + r ≡ 0 ( m o d p ) k * i + r \equiv 0 \pmod p k∗i+r≡0(modp),然后给两边同乘上 i i i 的逆元和 r r r 的逆元,
可化简得到: k ∗ r − 1 + i − 1 ≡ 0 ( m o d p ) k * r^{-1} + i ^{-1} \equiv 0 \pmod p k∗r−1+i−1≡0(modp)。
易知: k = [ p i ] ∗ , r = p m o d i k=\left[\dfrac{p}{i}\right] * ,\ r = p \bmod i k=[ip]∗, r=pmodi。
带入上柿可得:
i − 1 ≡ − [ p i ] ∗ ( p m o d i ) − 1 ( m o d p ) i^{-1} \equiv -\left[\dfrac{p}{i}\right] * (p \bmod i)^{-1} \pmod p i−1≡−[ip]∗(pmodi)−1(modp)
这样就可以用递推求出逆元了。
代码
建数组 i n v [ ] inv[] inv[], i n v i inv_i invi 表示 i i i 的逆元。就有:
inv[i] = -(p / i) * inv[p % i];
用这个方法过了模板题的代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rint register int
const int maxn = 3e6 + 5;
int inv[maxn], n, q;
signed main ()
{
scanf ("%lld %lld", &n, &q);
inv[1] = 1;
printf ("1\n");
for (rint i (2); i <= n; ++i)
{
inv[i] = ((-(q / i) * inv[q % i]) % q + q) % q;
printf ("%lld\n", inv[i]);
}
return 0;
}
—— E n d End End——