一、快速冪
原理:
快速冪的原理十分簡單。
ak=a2^0*a2^1*a2^2*…a2^x,其中k=20+21+22+…+2x。
這顯然是正確的。因為任何一個數都可以表示成二進制。
接下去利用位運算實現即可。
代碼實現
模板題鏈接:快速冪
代碼模板如下:時間復雜度O(logk)
int qmi(int a,int k,int p) { int res=1%p; while(k) { if(k&1)res=(long long)res*a%p; a=(long long)a*a%p; k>>=1; } return res; }
值得一提的是,以上代碼在過程中取模,是基於模運算的運算規則。
模運算有一些很好的性質,以下列舉四條:
- (a + b) % p = (a % p + b % p) % p
- (a - b) % p = (a % p - b % p + p) % p
- (a * b) % p = (a % p * b % p) % p
- (a^b) % p = ((a % p)^b) % p
二、快速冪求逆元
乘法逆元的定義
若整數b,m互質,並且b|a,則存在一個整數x,使得a/b≡a∗x(mod m),則稱x為b的模m乘法逆元,記為b−1(mod m)。
b存在乘法逆元的充要條件是b與模數m互質。當模數m為質數時,bm−2即為b的乘法逆元。
因為在模運算中,並沒有除法的性質,即沒有(a/b)%p≠((a%p)/(b%p))%p,而乘法逆元便可以幫助我們將其轉化成乘法形式:a/b % m=a∗x % m。
由乘法逆元的定義我們可以推出如下結論:
∵ a/b mod p = a * b-1 mod p = a/b * b * b-1 mod p
∴ b * b-1 ≡ 1(mod p)
費馬小定理
若p是一個質數,且整數a不是p的倍數(a與p互質),則有a p-1 ≡ 1(mod p)。
基於這個定理,我們就可以推出如下結論:
∵ a p-1 ≡ 1(mod p)
∴ a * a p-2 ≡ 1(mod p)
因此,結合乘法逆元的定義所得到的推論以及費馬小定理,我們可以得到:a-1 = a p-2(mod p)。
也就是說,當模數p為質數,且整數b不是p的倍數時(b與p互質),b的逆元即為b p-2。
代碼實現
模板題鏈接:快速冪求逆元
根據上述結論,要判斷b關於模數p的乘法逆元是否存在,若存在則求出之,我們便只需要計算b p-2即可。而這個計算利用快速冪便可以很快解決。
代碼如下:
#include <iostream> #include <algorithm> #include <cstdio> using namespace std; typedef long long ll; int qmi(int a,int k,int p) { int res=1%p; while(k) { if(k&1)res=(ll)res*a%p; a=(ll)a*a%p; k>>=1; } return res; } int main() { int n;scanf("%d",&n); for(int i=1;i<=n;i++) { int a,b; scanf("%d%d",&a,&b); if(a%b==0)printf("impossible\n"); else printf("%d\n",qmi(a,b-2,b)); } return 0; }
應用
有了乘法逆元,我們在計數類問題中遇到(a/b)%p時,便可以轉化成((a % p) * (b-1 % p)) % p來計算了,這樣便防止了爆int的情況出現,當然,轉化的前提是必須保證b與p互質。當p是質數時,則可以進一步轉化為((a % p) * (b p-2 % p)) % p。