定义
若 a ∗ x ≡ 1 ( m o d p ) a*x≡1 (mod\;p) a∗x≡1(modp),且 a a a 与 p p p 互质,那么我们就能定义: x x x 为 a a a 的逆元,记为 a − 1 a^{-1} a−1,所以我们也可以称 x x x 为 a a a 的倒数( m o d mod mod p p p 意义下)。
所以对于 a b m o d p \frac{a}{b}\ \; mod\;p ba modp ,我们就可以求出 b 在 m o d p mod\; p modp意义下的逆元,然后乘上 a a a ,再 m o d p mod \; p modp ,就是这个乘法逆元的值了。
模板题
原题链接》》乘法逆元
法一
两种证明:
(1)费马小定理:
定理内容:如果 a , p a,p a,p 互质,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1} ≡ 1 \;(mod\; p) ap−1≡1(modp)
把上式转换成 a ∗ a p − 2 ≡ 1 ( m o d p ) a*a^{p-2} ≡ 1 \;(mod\; p) a∗ap−2≡1(modp),结合逆元方程 a ∗ x ≡ 1 ( m o d p ) a*x ≡ 1 \;(mod\; p) a∗x≡1(modp),得到 a ∗ a p − 2 ≡ a ∗ x ( m o d p ) a*a^{p-2} ≡ a*x \;(mod\; p) a∗ap−2≡a∗x(modp)
所以 a p − 2 ≡ x ( m o d p ) a^{p-2} ≡ x \;(mod\; p) ap−2≡x(modp),即 x = a p − 2 m o d p x = a^{p-2} \;mod\; p x=ap−2modp, 用快速幂求解即可
(2)欧拉定理
定理内容:如果a,p互质,那么
a
φ
(
p
)
≡
1
(
m
o
d
p
)
a^{φ(p)}≡1(mod\;p)
aφ(p)≡1(modp),当p为质数时,
φ
(
p
)
=
p
−
1
φ(p)=p-1
φ(p)=p−1。
所以
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}≡1(mod\;p)
ap−1≡1(modp),跟费马小定理的式子长得一模一样,按刚才的方式推导即可得
x
=
a
p
−
2
m
o
d
p
x = a^{p-2} \;mod\; p
x=ap−2modp
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,p;
ll ksm(ll a,ll b)
{
ll ans=1,base=a;
while(b)
{
if(b&1)
ans=(ans*base)%p;
base=(base*base)%p;
b>>=1;
}
return ans;
}
int main(){
scanf("%lld%lld",&n,&p);
for(ll i=1;i<=n;i++)
{
printf("%lld\n",ksm(i,p-2)%p);
}
}
法二
解不定方程(扩欧)
解同余方程 a x ≡ 1 ( m o d p ) ax≡1 \;(mod \;p) ax≡1(modp)等价于解不定方程 a x + p y = 1 ax+py=1 ax+py=1
根据裴蜀定理,这个不定方程有整数解的前提是gcd(a,p)=1,即a,p互质。(而保证互质的一个方法是令a或b为一个大质数,比如998244353或者1e9+7之类的,所以如果题目莫名给了一个大质数或者a,b互质的条件,很可能就是在保证扩欧方程有解的前提)
E x g c d Exgcd Exgcd求解即可(不会 请左转 Exgcd )
code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,p,x,y;
void exgcd(int a,int b)
{
if(b==0)
{
x=1,y=0;
return;
}
exgcd(b,a%b);
int z=x;
x=y,y=z-(a/b)*y;
}
int main(){
scanf("%lld%lld",&n,&p);
for(ll i=1;i<=n;i++)
{
exgcd(i,p);
x=(x%p+p)%p;
printf("%lld\n",x);
}
}
法三
线性递推:复杂度 O ( n ) O(n) O(n)!!!
过程:
令
p
=
k
∗
i
+
r
p=k*i+r
p=k∗i+r ;
(
k
=
p
/
i
,
r
=
p
m
o
d
i
)
(k=p/i,r=p\;mod\;i\;)
(k=p/i,r=pmodi),其中
(
i
<
p
,
k
<
p
,
r
<
i
)
(i<p,k<p,r<i)
(i<p,k<p,r<i),则有 :
- k ∗ i + r ≡ 0 ( m o d p ) k*i+r≡0\;(mod\; p) k∗i+r≡0(modp)①
①式左右同乘 i − 1 ∗ r − 1 i^{-1}*r^{-1} i−1∗r−1得:
- k ∗ r − 1 + i − 1 ≡ 0 ( m o d p ) k*r^{-1}+i^{-1} ≡ 0 \;(mod \;p) k∗r−1+i−1≡0(modp)移项 得
移项 得
- i − 1 ≡ − k ∗ r − 1 ( m o d p ) i^{-1} ≡ -k*r^{-1} \;(mod\; p) i−1≡−k∗r−1(modp)
带入 k = p / i , r = p m o d i k=p/i,r=p\;mod\;i\; k=p/i,r=pmodi
得
- i − 1 ≡ ( − p i ) ∗ ( p m o d i ) − 1 ( m o d p ) i^{−1} ≡ (-\frac{p}{i})*(p\;mod \;i)^{-1} \;\;(mod\; p) i−1≡(−ip)∗(pmodi)−1(modp)②
由于 ( p m o d i ) < i (p\; mod\; i) < i (pmodi)<i 所以,在求出 i − 1 i^{-1} i−1之前,我们早已求出 ( p m o d i ) − 1 (p\; mod \;i)^{-1} (pmodi)−1因此用数组 i n v [ i ] 记录 i − 1 inv[i] 记录i^{-1} inv[i]记录i−1 ( i i i 的逆元),则
- i n v [ i ] ≡ ( − p i ) ∗ i n v [ p m o d i ] m o d p inv[i]≡ (-\frac{p}{i})*inv[p\;mod \;i] \;mod\; p inv[i]≡(−ip)∗inv[pmodi]modp
不要以为到这里就结束了
因为我们需要保证 i − 1 > 0 i^{-1}>0 i−1>0所以,我们在②式右边 + p ( p m o d p = 0 \;+p\; ( p\;mod\;p=0 +p(pmodp=0 , 答案不变)
即 i n v [ i ] = p − p i ∗ i n v [ p m o d i ] m o d p inv[i]=p-\frac{p}{i}\ * inv[p\;mod\;i]\;\;mod\;p inv[i]=p−ip ∗inv[pmodi]modp
当然 i n v [ 1 ] = 1 , i n v [ 0 ] = t a n 90 inv[1]=1,inv[0]=tan90 inv[1]=1,inv[0]=tan90(赋值为0);
而且 f o r for for循环 要从 2 2 2开始,防止改变 i n v [ 1 ] inv[1] inv[1] 的值;
code:
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,p,x,y,inv[3000010];
int main(){
scanf("%lld%lld",&n,&p);
inv[1]=1;
printf("%lld\n",inv[1]);
for(ll i=2;i<=n;i++)
{
inv[i]=p-(p/i)*inv[p%i]%p;
printf("%lld\n",inv[i]);
}
}
应用
一般用于求 a b m o d p \frac{a}{b}\ \;mod\; p ba modp ,把它转换为求a*(b的逆元)