逆元
逆元满足: a ∗ i n v ( a ) ≡ 1 ( m o d a*inv(a)≡1(mod a∗inv(a)≡1(mod p ) p) p)
1.模 p p p为素数的逆元
利用费马小定律:
a
p
−
1
≡
1
(
m
o
d
a^{p-1}≡1(mod
ap−1≡1(mod
p
)
p)
p)
则
i
n
v
(
a
)
=
a
p
−
2
inv(a)=a^{p-2}
inv(a)=ap−2
2.不要求模 p p p为质数的逆元求法
利用拓展欧几里得算法,既可以求合数模,也可以求素数模;并且常数较小,实际速度比快速幂更好一点,但快速幂好写一些。
给定模数
m
m
m,求
a
a
a的逆相当于求解
a
x
=
1
(
m
o
d
ax=1(mod
ax=1(mod
m
)
m)
m)
这个方程可以转化为
a
x
−
m
y
=
1
ax-my=1
ax−my=1
然后套用求二元一次方程的方法,用扩展欧几里得算法求得一组
x
0
,
y
0
x0,y0
x0,y0和
g
c
d
gcd
gcd
检查
g
c
d
gcd
gcd是否为1
g
c
d
gcd
gcd不为1则说明逆元不存在
若为
1
1
1,则调整
x
0
x0
x0到
0
0
0~
m
−
1
m-1
m−1的范围中即可
void exgcd(int a,int b,int &d,int &x,int &y){
if (!b) d=a, x=1, y=0;
else exgcd(b,a%b,d,y,x), y-=a/b*x;
}
ll inv(int a, int mod) {
ll ret, d, y;
exgcd(a,mod,d,ret,y);
return d==1?(ret+mod)%mod:-1;
}
3.递归求逆元
递推公式: i n v [ 1 ] = 1 inv[1]=1 inv[1]=1, i n v [ i ] = ( m o d − m o d / i ) ∗ i n v [ m o d inv[i]=(mod-mod/i)*inv[mod inv[i]=(mod−mod/i)∗inv[mod% i ] i] i]% m o d ; mod; mod;
inv[1]=1;
for(int i=2; i<mod; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
也可用于单次求逆元(但一般不用)
ll inv(ll a, ll m) { return a==1?1:(m-m/a)*inv(m%a,m)%m; }
4.利用递归预处理阶乘的逆元
inv[0]=inv[1]=1;
for(int i=2; i<mod; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=2; i<mod; ++i) inv[i]=inv[i]*inv[i-1]%mod;