由于模意义下的除法运算不具有封闭性,所以我们得找一种办法将除法转换成其他的运算,下面就介绍了在模意义下取代除法运算的一种方法--乘法逆元。
定义:若整数b,m互质,并且b|a,则存在一个整数x使得$ frac{a}{b} equiv a*x (mod m) $ ,称x为b的模m的乘法逆元,记为$b^{-1}(mod m) $,也可以记为inv(b)。(倒数)
因为 $$ a/b equiv a*b^{-1} equiv a/b *b *b^{-1} (mod m ) $$
所以 $ b^{-1}*b equiv 1 (mod m) $
若m为质数且b<m,根据费马小定理$ b^{m-1} equiv 1 (mod m) $,所以 $b*b^{m-2} equiv 1 (mod m) $
所以当模数为质数的时候, (b^{m-2}) 为b模m的乘法逆元。
如果只是保证b,m互质,那么可以通过求解同余方程 (b*x equiv 1 (mod m)) 得到b的模m的乘法逆元x,可以用扩展欧几里得算法解。
因为
所以
通过求解上面的方程,可以求得一组x,y。x则为b模m的逆元
下面是模板
费马小定理求逆元
inline lxl Quick_Pow(lxl a, lxl b,lxl mod) {
lxl res(1 % mod);
a%=mod;//保险起见
for(; b; b >>= 1) {
if(b & 1)
res = res * a % mod;
a = a * a % mod;
}
return res % mod;
}
inline lxl Fermat_inv(lxl a,lxl mod){
return Quick_Pow(a,mod-2,mod);
}
扩展欧几里得求逆元
int exgcd(int a, int b, int &x, int &y) {
if(!b) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int z = x;
x = y;
y = z - (a / b) * y;
return d;
}
int exgcd_inv(int a){
int d,x,y;
d=exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
有了乘法逆元,我们遇到a/b的式子后,可以先a,b各自对模数取模,在计算 $ a*b^{-1} mod m $作为答案(前提是b,p互质或p是质数)
若是要求1n每个数关于模p的逆元,这时候一个个求就显得比较慢了,下面介绍一种求1n每个数模p的逆元的线性求法
首先 ,(1^{-1} equiv 1 (mod p))
然后我们设(p=k*i+r, 0le r <i, 1<i<p)
所以 (k*i+r equiv 0 (mod p))
然后两边同乘 (i^{-1},r^{-1}) 得到
inv[1]=1;
rep(i,2,n){
inv[i]=(mod-[mod/i])*inv[mod%i]%mod;
}//前面加了mod是为了防止逆元变成负数,而且这样常数也比较小
求特定几个组合数的时候,我们需要求出一些连续的阶乘的逆元,我们也可以做到近线性求法
所以我们可以得到以下做法
fac[0] = 1;//规定0!=1
rep(i, 1, n) {
fac[i] = (fac[i - 1] * i) % mod;
}
inv_fac[MAX] = Fermat_inv(fac[n], mod );
drp(i, n - 1, 0) {
inv_fac[i] = inv_fac[i + 1] * (i + 1) % mod;
}
小技巧
求$ frac{a}{b} (mod p ) $ 如果 p * b 不会爆掉,可以写成 $$ frac{ a mod (b imes q) } {b} (mod p) $$
适用于任何情况下。没有逆元可以这样用
多项式求逆元