乘法逆元
如果一个线性同余方程 a x ≡ 1 ( m o d n ) ax\equiv 1(mod\ n) ax≡1(mod n),则 x x x称为 a m o d n a\ mod\ n a mod n的逆元,记作 a − 1 a^{-1} a−1。
扩展欧几里得算法求逆元
若 g c d ( a , n ) ≠ 1 gcd(a,n)\not ={1} gcd(a,n)=1,则方程无解。所以如果方程有解的话,就可以通过扩展欧几里得算法求逆元。
快速幂法求逆元
根据费马小定理,若 n n n 为素数,且 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1 , a m o d n a\ mod\ n a mod n的逆元可以计算为 a n − 2 a^{n-2} an−2,可通过快速幂求解。
求 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n中每个数关于 p p p的逆元
算法时间复杂度为 O ( n ) O(n) O(n)。
gg inv[MAX];
void invEle(gg n, gg p) {
inv[1] = 1;
for (gg i = 2; i <= n; ++i) {
inv[i] = (p - p / i) * inv[p % i] % p;
}
}
求任意 n n n 个数关于 p p p的逆元
首先计算 n 个数的前缀积,记为 s i s_i si,然后利用快速幂或扩展欧几里得算法计算 s n s_n sn的逆元,记作 s v n sv_n svn。因为 s v n sv_n svn 是 n 个数的积的逆元,所以当我们把它乘上 a n a_n an 时,就会和 a n a_n an 的逆元抵消,于是就得到了 a 1 a_1 a1 到 a n a_n an 的积逆元,记为 s v n − 1 sv_{n-1} svn−1。可以依次计算出所有的 s v i sv_i svi ,于是 a i − 1 a_i^{-1} ai−1 就可以用 s i − 1 ⋅ s v i s_{i-1}\cdot sv_i si−1⋅svi 求得。算法时间复杂度为 O ( n + l o g p ) O(n+log\ p) O(n+log p)
gg inv[MAX], s[MAX], a[MAX], sv[MAX];
void invEle(gg n, gg p) {
s[0] = 1;
for (gg i = 1; i <= n; ++i) {
s[i] = s[i - 1] * a[i] % p;
}
//也可以用扩展欧几里得来求逆元,视个人喜好而定.
sv[n] = powMod(s[n], p - 2, p);
for (gg i = n; i >= 1; --i) {
sv[i - 1] = sv[i] * a[i] % p;
}
for (gg i = 1; i <= n; ++i) {
inv[i] = sv[i] * s[i - 1] % p;
}
}