题解:P3811 【模板】模意义下的乘法逆元
update on 2025.10.29:之前写得比较潦草,现在重新回来修改。
博客食用效果更佳:link。
定义
若
接下来我要证明一件事情,我看很多题解里都没有证明,那就是题目让我们输出
首先
解法
题目中说明了
我们设
使用时我们调用
这里给出模板题目:link。
代码
#include<bits/stdc++.h>
#define int long long
int n,p,f[3000005];
signed main(){
//ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
//freopen("","r",stdin);
//freopen("","w",stdout);
cin>>n>>p;
f[1]=1;
cout<<f[1]<<"\n";
for(int i=2;i<=n;i++){
f[i]=(-p/i*f[p%i]%p+p)%p;
cout<<f[i]<<"\n";
}
return 0;
}
代码中需要注意如果是负数那么我们需要给它加
显然代码中一层循环,复杂度为
线性递推的解法对于求解多个逆元显然很有效,但是对于单组逆元却显得有些多余,于是我们有求单组逆元的两种解法。
费马小定理
前面提到了对于
定理本身
对于质数
定理证明
众所周知,我们还有一个著名的基本数论定理:欧拉定理。
欧拉定理
对于正整数
其中
证明
引理:令
引理证明:
正难则反,考虑使用反证法。
假设存在一个正整数
那么,由于
故,原命题成立。
欧拉定理证明:
证明了引理以后,欧拉定理也就很好证明了。
由引理可得,设
然后,由于
证毕!
与费马小定理的关系
很显然,欧拉定理就是费马小定理的加强版,易知
求解逆元
根据费马小定理我们有:
即
由乘法逆元的定义可知
于是,我们就得出了如何求出
代码实现
问题被我们转化为了求
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,p;
int powmod(int a,int b,int mod){
int res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
signed main(){
cin>>a>>p;
cout<<powmod(a,p-2,p);
return 0;
}
算法复杂度为
扩展欧几里得
内容
在讲扩展欧几里得算法之前,我们先要引出一个著名数论定理 Bézout 定理,关于它我也有讲解,可以看这篇博客:link。
我们考虑不定方程
- 显然当
b=0 时,有\left\{\begin{matrix}x=1\\y=0\end{matrix}\right. 满足条件。 - 当
b\ne 0 时,我们根据欧几里得算法有\gcd(a,b)=\gcd(b,a\bmod b) 于是,我们就有(a\bmod b)y+bx=\gcd(b,a\bmod b) 又由于(a\bmod b)y+bx=\left(a-b\times\lfloor\frac{a}{b}\rfloor\right)\times y+bx 将\operatorname{RHS} 展开,合并同类项后有(a\bmod b)y+bx=ay+b\times \left(x-\lfloor\frac{a}{b}\rfloor y\right) 于是,我们令x_0=y,y_0=x-\lfloor \frac{a}{b}\rfloor y 就有ax_0+by_0=\gcd(a,b) 。
代码实现
根据上述内容,我们可以打出扩展欧几里得算法的代码:
int exgcd(int a,int b,int&x,int&y){
if(b==0){
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int z=x;
x=y;
y=z-z*y;
return d;
}
功能介绍
以上函数的返回值为 x 与 y 的值,而函数运行完成后 x 与 y 所保存的值就是
与乘法逆元的联系
扩展欧几里得定理一般处理线性同余方程,我在这篇博客中有介绍:link,这里求乘法逆元也就相当于求关于
由线性同余方程的性质可以得到,方程有解当且仅当
又由于方程可以写成
代码实现
根据前面的内容可以打出代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b;
int exgcd(int a,int b,int&x,int&y){
if(b==0){
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;
}
signed main(){
cin>>a>>b;
int x,y;
exgcd(a,b,x,y);
cout<<(x%b+b)%b;
return 0;
}