逆元详解

逆元作用

模意义下的除法在大多数时候都不适用。

当题目中要求对答案取模,但我们又不得不使用一般的除法的时候,就需要用逆元的乘法来代替除法。

逆元定义

在模意义下,a mod m的逆元(下文中用inv[a]代替)就是一个数inv[x]使得:

inv[x] ≡ x-1 (mod m)

那么除法就可以转换成:

a / b = a * b-1 ≡ a * inv[b] (mod m)

注意逆元是定义在x与m互质的情况下的,也就是当x与m不互质时,x模m的逆元不存在。

逆元求法

1.费马小定理And欧拉定理

费马小定理是我最常用的,只要互素就可以用了,一般而言模数是素数时只要a不是模数的倍数就可以使用了。

时间复杂度 O ( l o g n ) O(logn) O(logn),适用于大多数时候

在m为素数时
a m − 1 ≡ 1   ( m o d   m ) a^{m-1}≡ 1 (mod m) am11 (mod m)

左右同时乘上 i n v [ a ] inv[a] inv[a]

i n v [ a ] ∗ a m − 1 ≡ i n v [ a ]   ( m o d   m ) inv[a] * a^{m-1} ≡ inv[a]  (mod m) inv[a]am1inv[a] (mod m)

i n v [ a ] inv[a] inv[a]换成 a − 1 a^{-1} a1可得:

a − 1 ∗ a m − 1 ≡ a m − 2 ≡ i n v [ a ]   ( m o d   m ) a^{-1} * a^{m-1} ≡ a^{m-2} ≡ inv[a]  (mod m) a1am1am2inv[a] (mod m)

好的那么 a mod m 的逆元就是:

i n v [ a ] = a m − 2 % m inv[a] = a^{m-2}\%m inv[a]=am2%m

那么欧拉定理也是一样的 a ϕ ( m ) ≡ 1   ( m o d   m ) a^{\phi(m)}≡ 1 (mod m) aϕ(m)1 (mod m)
同理可得 i n v [ a ] = a ϕ ( m ) − 1 % m inv[a] = a^{\phi(m)-1}\%m inv[a]=aϕ(m)1%m
欧拉定理适用于m不是素数的情况
在做模数可变的时候比较可靠(但是一般要利用欧拉函数是积性函数打欧拉筛搞那个 p h i phi phi

typedef long long LL;
LL Pow(LL x,LL p,int mod) {
	if(p==0) return 1;
	if(p%2==0) return Pow(x*x%mod,p/2,mod);
	return x*Pow(x*x%mod,p/2,mod)%mod;
}
inv[a]=Pow(a,m-2,m);          //MOD is prime
inv[a]=Pow(a,phi[m]-1,m);     //init -> phi

关于欧拉函数的计算戳这里

2.拓展欧几里得算法

复杂度 O ( l o g n ) O(logn) O(logn)
适用性强于费马小定理
但是其实感觉还是 Pow 比 Exgcd 好打一点哈

当 a 与 b 互素时有 ( a , b ) = 1 ;

即得: a ∗ x + b ∗ y = 1 a * x + b * y = 1 ax+by=1

a ∗ x ≡ 1   ( m o d   b ) a * x ≡ 1  ( mod b ) ax1 (mod b)

i n v [ a ] ∗ a ∗ x ≡ i n v [ a ]   ( m o d   b ) inv[a] * a * x ≡ inv[a]  ( mod b ) inv[a]axinv[a] (mod b)

i n v [ a ] ≡ x   ( m o d   b ) inv[a] ≡ x  ( mod b ) inv[a]x (mod b)

因此 x 是 a mod b 的逆元;

蒟蒻表示自己并不怎么会Exgcd
挖下巨坑以后再补哈哈

线性逆元

复杂度 O ( n ) O(n) O(n)
当需要很多数的逆元时,例如我们需要前n个数的逆元时
如果用前两个算法挨个计算,对于前n个数会是 O ( n l o g n ) O(nlogn) O(nlogn)的复杂度
介绍一种线性逆元算法:
原理:假设我们需要求 x mod m 的逆元
首先我们需要一句废话: m ≡ 0   ( m o d   m ) m ≡ 0  ( mod m ) m0 (mod m)
我们知道: m = ⌊ m x ⌋ ∗ x + m % x m =\lfloor \frac{m}{x} \rfloor *x+m \% x m=xmx+m%x
所以考虑让x“落单”,我们可以先移项 ⌊ m x ⌋ ∗ x ≡ − m % x   ( m o d   m ) \lfloor \frac{m}{x} \rfloor *x ≡ -m \% x  ( mod m ) xmxm%x (mod m)
接着可以使用带入逆元了 ⌊ m x ⌋ ∗ x ∗ i n v [ x ] ≡ ( − m % x ) ∗ i n v [ x ]   ( m o d   m ) \lfloor \frac{m}{x} \rfloor *x*inv[x] ≡ (-m \% x)*inv[x]  ( mod m ) xmxinv[x](m%x)inv[x] (mod m)
⌊ m x ⌋ ≡ ( − m % x ) ∗ i n v [ x ]   ( m o d   m ) \lfloor \frac{m}{x} \rfloor ≡ (-m \% x)*inv[x]  ( mod m ) xm(m%x)inv[x] (mod m)
然而好像不好看,考虑利用m%x的逆元转换到x的逆元(左边是为了避免负数加了个m)
m − ⌊ m x ⌋ ≡ ( m % x ) ∗ i n v [ x ]   ( m o d   m ) m-\lfloor \frac{m}{x} \rfloor ≡ (m \% x)*inv[x]  ( mod m ) mxm(m%x)inv[x] (mod m)
( m − ⌊ m x ⌋ ) ∗ i n v [ m % x ] ≡ ( m % x ) ∗ i n v [ x ] ∗ i n v [ m % x ]   ( m o d   m ) (m-\lfloor \frac{m}{x} \rfloor )*inv[m \% x] ≡ (m \% x)*inv[x]*inv[m \% x]  ( mod m ) (mxm)inv[m%x](m%x)inv[x]inv[m%x] (mod m)
i n v [ x ] ≡ ( m − ⌊ m x ⌋ ) ∗ i n v [ m % x ]   ( m o d   m ) inv[x] ≡ (m-\lfloor \frac{m}{x} \rfloor )*inv[m \% x]  ( mod m ) inv[x](mxm)inv[m%x] (mod m)
我们惊奇地发现 x > m % x   恒 成 立 x>m\%x 恒成立 x>m%x 
这就意味着我们以一种DP的思路从小到大更新inv数组,可以以 O ( n ) O(n) O(n)的复杂度更新出来
i n v [ x ] ≡ ( m − ⌊ m x ⌋ ) ∗ i n v [ m % x ] % m inv[x] ≡ (m-\lfloor \frac{m}{x} \rfloor )*inv[m \% x] \% m inv[x](mxm)inv[m%x]%m

typedef long long LL;
inv[1]=1;
for(int i=2;i<=n;i++)
	inv[i]=((LL)(MOD-MOD/i)*inv[MOD%i])%MOD;

线性阶乘逆元

复杂度 O ( n ) O(n) O(n)
在求组合数的那些问题时,往往数比较大,涉及除法,有大量阶乘运算
在这里定义 x ! x! x! mod m 的逆元为 i n v [ x ] inv[x] inv[x](不要和上面搞混了)
好的首先我们知道:
( x ! ) − 1 = [ ( x + 1 ) ! ] − 1 ∗ ( x + 1 ) (x!)^{-1}=[(x+1)!]^{-1}*(x+1) (x!)1=[(x+1)!]1(x+1)
那么这个逆元其实很简单啦
i n v [ x ] = i n v [ x + 1 ] ∗ ( x + 1 ) inv[x]=inv[x+1]*(x+1) inv[x]=inv[x+1](x+1)
不过这个好像是由x+1推到x
所以我们可以用先前的办法 O ( l o g n ) O(logn) O(logn)求出 i n v [ x ] inv[x] inv[x](当然你需要先求到x的阶乘)
然后就可以倒退着把所有的逆元求出来了

LL Pow(LL x,LL p,int mod) {
	if(p==0) return 1;
	if(p%2==0) return Pow(x*x%mod,p/2,mod);
	return x*Pow(x*x%mod,p/2,mod)%mod;
}
x=1;
for(int i=2;i<=n;i++)
	x=((LL)x*i)%m;
inv[n]=Pow(x,m-2,m);
for(int i=n-1;i>=1;i--)
	inv[i]=((LL)inv[i+1]*(i+1))%m;

你可能感兴趣的:(数论)