逆元作用
模意义下的除法在大多数时候都不适用。
当题目中要求对答案取模,但我们又不得不使用一般的除法的时候,就需要用逆元的乘法来代替除法。
逆元定义
在模意义下,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)
am−1≡1 (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]∗am−1≡inv[a] (mod m)
把 i n v [ a ] inv[a] inv[a]换成 a − 1 a^{-1} a−1可得:
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) a−1∗am−1≡am−2≡inv[a] (mod m)
好的那么 a mod m 的逆元就是:
i n v [ a ] = a m − 2 % m inv[a] = a^{m-2}\%m inv[a]=am−2%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 a∗x+b∗y=1
a ∗ x ≡ 1 ( m o d b ) a * x ≡ 1 ( mod b ) a∗x≡1 (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]∗a∗x≡inv[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 )
m≡0 (mod m)
我们知道:
m
=
⌊
m
x
⌋
∗
x
+
m
%
x
m =\lfloor \frac{m}{x} \rfloor *x+m \% x
m=⌊xm⌋∗x+m%x
所以考虑让x“落单”,我们可以先移项
⌊
m
x
⌋
∗
x
≡
−
m
%
x
(
m
o
d
m
)
\lfloor \frac{m}{x} \rfloor *x ≡ -m \% x ( mod m )
⌊xm⌋∗x≡−m%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 )
⌊xm⌋∗x∗inv[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 )
m−⌊xm⌋≡(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 )
(m−⌊xm⌋)∗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]≡(m−⌊xm⌋)∗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]≡(m−⌊xm⌋)∗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;