乘法逆元学习笔记
一、乘法逆元
如線性同余方程 ax≡1(modb)ax\equiv 1\pmod bax≡1(modb) 的解 xxx 稱作 aaa 在模 bbb 下的逆元,記作 x=a?1x=a^{-1}x=a?1。
二、乘法逆元存在的條件
根據線性同余方程組解存在的條件,當且僅當 gcd?(a,b)=1\gcd(a,b) =1gcd(a,b)=1 時,aaa 在模 bbb 下存在逆元。
三、求解逆元
1. 擴展歐幾里得算法
使用擴展歐幾里得算法可以在 O(log?b)O(\log b)O(logb) 的時間復雜度下求出單個數的逆元。
求解線性同余方程 ax≡1(modb)ax\equiv 1\pmod bax≡1(modb) 即可。
2. 快速冪
使用快速冪可以在 O(log?n)O(\log n)O(logn) 的時間復雜度下求出單個數的逆元(模數應為質數)。
當 bbb 為質數時,由費馬小定理得 ab?1≡1(modb)a^{b-1}\equiv 1 \pmod bab?1≡1(modb) ,因此 a×ab?2≡1(modb)a \times a^{b-2} \equiv 1 \pmod ba×ab?2≡1(modb) ,即 aaa 的逆元為 ab?2a^{b-2}ab?2 。代碼如下:
3. 線性求逆元
使用線性求逆元的方法可以在 O(b)O(b)O(b) 的時間復雜度下求出 111 ~ b?1b-1b?1 所有數在模 bbb 下的逆元。
從 111 到 b?1b-1b?1 遞推。
① 111 的逆元為 111
② 設當前數為 iii ,且 b=ki+r(r<i)b=ki+r(r<i)b=ki+r(r<i) ,則 bmodi=rb\bmod i = rbmodi=r 。
在模 bbb 意義下:
ki+r≡0(modb)ki+r\equiv 0\pmod bki+r≡0(modb)
兩邊同時乘以 i?1r?1i^{-1}r^{-1}i?1r?1 得:
kr?1+i?1≡0(modb)kr^{-1}+i^{-1}\equiv0\pmod bkr?1+i?1≡0(modb)
移項整理:
i?1≡?kr?1≡??bi?(bmodi)?1(modb)i^{-1}\equiv -kr^{-1}\equiv -\lfloor\frac{i}\rfloor (b\bmod i)^{-1}\pmod bi?1≡?kr?1≡??ib??(bmodi)?1(modb)
由于 bmodi<ib\bmod i < ibmodi<i ,因此已知,可以 O(1)O(1)O(1) 遞推。代碼如下(Luogu 3811 【模板】 乘法逆元):
#include<bits/stdc++.h> using namespace std; const int maxn = 3e6 + 5; int n, p; int inv[maxn]; int main(){ios::sync_with_stdio(false);cin.tie(0);cin >> n >> p;inv[1] = 1;for (int i = 2; i <= n; i ++) inv[i] = (p - (p / i)) * inv[p % i] % p;for (int i = 1; i <= n; i ++) cout << inv[i] << '\n';return 0; }4. 線性求逆元(任意)
接下來介紹一種可以在幾乎線性的時間內求出給出數列 {an}\{a_n\}{an?} 中所有數在模 bbb 意義下的逆元的方法。
首先計算 {ai}\{a_i\}{ai?} 的前綴積,記為 {sumn}\{sum_n\}{sumn?} ,即 sumi=Πj=1iajsum_i=\Pi_{j=1}^{i} a_jsumi?=Πj=1i?aj? 。其次計算 sumnsum_nsumn? 的逆元,記為 invsninvs_ninvsn? 。將 invsninvs_ninvsn? 倒推求出每一個前綴積的逆,記作 {invsn}\{invs_n\}{invsn?} ,遞推公式為:
invsi?1=invsi?aiinvs_{i-1} = invs_{i} * a_iinvsi?1?=invsi??ai?
有了兩個數組,就可以據此得出所有數的逆元 {invi}\{inv_i\}{invi?} :
invi=invsi?1×sumiinv_i=invs_{i-1}\times sum_iinvi?=invsi?1?×sumi?
代碼如下(Luogu 5431 【模板】 乘法逆元2):
/*提示:題目卡常,需要使用快讀 */ #include<bits/stdc++.h> using namespace std; const int maxn = 5e6 + 5;long long n, p, k; long long a[maxn], sum[maxn], invs[maxn];template <typename T> inline void read(T &x){char c;x = 0;int fu = 1;c = getchar();while(c > 57 || c < 48){if(c == 45) fu = -1;c = getchar();}while(c <= 57 && c >= 48){x = (x << 3) + (x << 1) + c - 48;c = getchar();}x *= fu; }long long qPow(long long a, long long b){long long ret = 1;for (; b; b >>= 1){if (b & 1) ret = (ret * a) % p;a = (a * a) % p;}return ret; }int main(){read(n), read(p), read(k);for (int i = 1; i <= n; i ++) read(a[i]);sum[0] = 1;for (int i = 1; i <= n; i ++) sum[i] = sum[i - 1] * a[i] % p;invs[n] = qPow(sum[n], p - 2);for (int i = n - 1; i >= 1; i --) invs[i] = invs[i + 1] * a[i + 1] % p;long long mulk = 1, ans = 0;for (int i = 1; i <= n; i ++){mulk = mulk * k % p;ans = (ans + mulk * (invs[i] * sum[i - 1] % p) % p) % p;}printf("%lld\n", ans);return 0; }總結
- 上一篇: MNE学习笔记(四):Evoked数据结
- 下一篇: 信访案件管理系统 下载