逆元的意義:
通俗的講,逆元可以看做一個數的倒數的整數形式,但是一個數的逆元在不同的 $ (mod) $ 意義下是不一樣的。
$ a\times x\equiv1 \mod n \quad $ ☞ $ \quad a\times \frac{1}{a}\equiv1 \mod n $
這個方程便是逆元的真正定義, $ x $ 的解即代表 $ a $ 在 $ \mod n $ 意義下的逆元,通俗的講:此時的 $ x $ 就相當於 $ a $ 的倒數,這樣 $ a\times x $ 便會等於1,在 $ \mod n $ 意義下余數必定為一。當然這個式子要建立在 $ a $ 與 $ n $ 互質的基礎上!
可是逆元有什么用呢?直接用倒數不行嗎?這是因為我們發現一個分數 $ mod $ 一個整數時是不能直接模運算的,但是可以進行乘法運算,我們就要用到逆元(一個數倒數的整數形式)
就像: $ \frac{a}{b}\mod (n) $ $ \not = $ $ \frac{a\mod n}{b\mod n}\mod (n) $ 但是: $ \frac{a}{b}\mod (n) $ $ = $ $ a\times b^{-1}\mod (n) $
所以當除運算碰上我們的模運算時,我們就需要 $ \mod 模數 $ 意義下的逆元了!
單個逆元的求法:
1. 費馬小定理:
對於整數 $ a $ 與質數 $ p $ ,若 $ a $ 與 $ p $ 互質,則有: $ a^{p-1}\equiv1 \mod p $
我們將上述定理稍稍變一下: $ a\times a^{p-2}\equiv1 \mod p $ (這不就是我們的逆元定義式嗎?)
所以 $ a^{p-2} $ 就是 $ a $ 在 $ \mod p $ 意義下的逆元啊!這個我們用快速冪求一下不就行了嗎!
inline ll fast(ll x){//求x在%mod意義下的逆元
int y=mod-2;ll res=1;
while(y){
if(y&1)res=res*x%mod;
x=x*x%mod; y>>=1;
}return res;
}
2. 拓展歐幾里得:
用快速冪求逆元,是敵不過某些毒瘤出題人,比如模數就是個long long,這樣快速冪就會溢出。這個時候怎么辦?嗯,我們的萬能 $ gcd $ 就要登場了!(請不要理會博主的中二病,然后防爆longlong還可以用快速乘的)
- 存在整數 $ x_1 $ 和 $ y_1 $ ,使得: $ x_1\times a+y_1\times b=gcd(a,b) $
- 同理,存在整數 $ x_2 $ 和 $ y_2 $ ,使得: $ x_2\times b+y_2\times (a\mod b) =gcd(b,a\mod b) $
根據我們萬能 $ gcd $ 的性質,上述兩個等式的右半部分相同,同理它們的左半部分也相同!
於是我們得到:
- $ x_1\times a+y_1\times b=x_2\times b+y_2\times (a\mod b) $
- $ x_1\times a+y_1\times b=x_2\times b+y_2\times (a-\frac{a}{b}\times b) $
- $ x_1\times a+y_1\times b=y_2\times a+(x_2-y_2\times \frac{a}{b})\times b $
- $ x_1=y_2 \quad $ $ \quad y_1=x_2-y_2\times \frac{a}{b} $
所以我們根據 $ x_2 $ 和 $ y_2 $ 就能求出 $ x_1 $ 和 $ y_1 $ 辣!而眾所周知的,我們 $ gcd $ 大法的終極形態就是當 $ b0 $ 的時候,這時我們的 $ x=1 $ ,而因為 $ b0 $ ,所以我們的 $ y $ 隨便取一個就行(好像都用0的),然后我們在回溯的時候,不斷往前推我們的 $ x $ 和 $ y $ ,直到得出我們最初的哪一組解。(這其實就是一個構造的過程)
可是這和求逆元又有什么聯系呢?我們發現只有當我們的 $ a $ 與 $ b $ 互質的時候即 $ gcd(a,b)=1 $ 時,這個二元一次方程不就變成了: $ x_1\times a+y_1\times b=1 $ (等同於 $ x_1\times a=1(\mod b) $ 或 $ y_1\times b=1(\mod a) $ )而這,不就相當於我們逆元的定義式嗎?我們再用上述方法將 $ x_1 $ 和 $ y_1 $ 求出來,不就是逆元了嗎?
inline int exgcd(int a,int b,int &x,int &y){
if(b==0)return x=1,y=0,a;
int d=exgcd(b,a%b,y,x);
y-=(a/b)*x; return d;
}
遞推求多個逆元:
上面我們說的是單個求逆元的方法,復雜度為 $ O(logn) $ 級別(一般快速冪要快一些),可是有一些題目往往需要我們求多個逆元(如連續的數,階乘,和次方的逆元),而且單個求的時間往往要在 $ O(1) $ 的復雜度,我們有沒有辦法 $ O(n) $ 求出所有這些逆元呢?答案是可以的,但只能是一些特殊的數。
線性求逆元:
同余是神奇的,我們可以通過它推出一個逆元的遞推式:
$ \Rightarrow inv[i]=(m-m/i)\times inv[m \mod i] \mod m $
推導過程:設 $ a=m/i $ ,設 $ b=m \mod i $ ,則有 $ m=a\times i+b $
$ \Rightarrow a\times i+b\equiv 0\mod(m) $
$ \Rightarrow -a\times i\equiv b\mod(m) $
我們將同余號兩邊都除以一個 $ i\times b $ ,把逆元這個概念引進來,得到:
$ \Rightarrow -a\times inv[b]\equiv inv[i]\mod(m) $
這樣,我們就將 $ i $ 和 $ m \mod i $ 聯系到了一起,我們將 $ a $ 和 $ b $ 換回原裝可得:
$ \Rightarrow -m/i\times inv[m \mod i]\equiv inv[i]\mod(m) $
$ \Rightarrow inv[i]\equiv (m-m/i)\times inv[m \mod i] \mod(m) $
$ \Rightarrow inv[i]=(m-m/i)\times inv[m \mod i] \mod m $
這樣,只要我們預處理一下 $ inv[1]=1 $ ,往后的所有逆元我們都能線性求了!
int main(){
n=qr(),m=qr(); inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(ll)(m-m/i)*inv[m%i]%m;
return 0;
}
線性求階乘逆元: (這個比上面的要簡單一些。)
我們都知道,階乘代表: $ n!=1\times 2\times 3...\times n $ ,那么我們可以得出: $ (n-1)!=n!/n $
這不也是一個逆元式嗎?只不過我們要倒着遞推而已。
我們先用快速冪或拓展歐幾里得求出 $ n! $ 的逆元,然后所有比它小的逆元都可以線性推過去了!
int main(){
n=qr(),m=qr(); jc_inv[n]=1;
for(int i=n;i>=2;--i)
jc_inv[i-1]=(ll)jc_inv[i]*i%m;
return 0;
}