乘法逆元,就是有ab ≡ 1 mod p,则b就是mod p意义下乘法的逆元,即b=inv(a)。
逆元的意义就是在模意义下,即剩余系中,除法是没有封闭性的,很有可能造成溢出,所以用
乘法逆元代替除法。因为本人是蒟蒻,这里就记一下求逆元的方法和模板了。
1.递推求乘法逆元。
在O(n)时间内可以推出1~n 在模p意义下的逆元。方法如下:
所以线性递推式为
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
如何利用呢?我们可以利用这个求组合数在mod意义下的值,函数如下:
const ll mod=1e9+7;
const ll maxn=1e5+1;
ll f[maxn]={1,1};
ll f0[maxn]={1,1};
ll inv[maxn]={1,1};
void init()
{
for(int i=2;i<=maxn;i++)
{
//阶乘数
f[i]=f[i-1]*i%mod;
//i在mod意义下的逆元
f0[i]=(mod-mod/i)*f0[mod%i]%mod;
//阶乘逆元
inv[i]=inv[i-1]*f0[i]%mod;
}
}
//求阶乘数C(a,b)在mod意义下的值
ll C(ll a,ll b)
{
return f[b]*inv[a]%mod*inv[b-a]%mod;
}
是不是清晰明了?这儿有道模板题,代码补全就能提交~
2.扩展欧几里得求逆元
ax≡1(modp)可以等价的转化为ax−py=1
然后套用exgcd解方程,并检查gcd(a,p)是否等于1
如果gcd(a,p)=1,把x调整到1~p−1即可
复杂度O(log n) 。
3.费马小定理
由于,所以,所以i的逆元就是模p意义下的,
即,所以递推就行。
4.欧拉定理
,所以就是a的逆元。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
int inv[1000010];
LL ksm(LL a,LL b,LL mod)
{
int ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
LL GCD=exgcd(b,a%b,x,y);
LL tmp=x;
x=y;
y=tmp-a/b*y;
return GCD;
}
LL inv1(LL a,LL mod)//扩展欧几里得求逆元
{
LL x,y;
LL d=exgcd(a,mod,x,y);
if(d==1) return (x%mod+mod)%mod;
return -1;
}
LL inv2(LL a,LL mod)//费马小定理
{
return ksm(a,mod-2,mod);
}
void inv3(LL mod)//线性递推求逆元
{
inv[1]=1;
for(int i=2;i<=mod-1;i++)
{
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
cout<<inv[i]<<" ";
}
}
int main()
{
LL n,mod;
while(cin>>n>>mod)
{
cout<<inv1(n,mod)<<" "<<inv2(n,mod)<<endl;
inv3(mod);
}
}