拓展欧几里得求逆元
原理
\(a*b\equiv 1 \pmod p\)
\(a*b+k*p = 1\)
代码
//拓展欧几里德求逆元
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1;y=0;
return a;
}
int ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
int getinv(int a,int m=mod){
int x,y;
int res=exgcd(a,m,x,y);
return res==1? (x%m+m)%m:-1;
}
性能分析
时间复杂度:O(logn)(实际是斐波那契数列)
适用范围:只要存在逆元即可求,适用于个数不多但是mod很大的时候,也是最常见的一种求逆元的方法。
费马小定理
原理
当p为素数时
\(a^{p-1}\equiv1\pmod p\)
\(a^{p-2}*a\equiv 1 \pmod p\)
代码
//费马小定理/快速幂求逆元
int ksm(int a,int b=mod-2,int m=mod){
int res=1;
while(b){
if(b&1){
res*=a;
res%=m;
}
a*=a;
a%=m;
b>>=1;
}
return res;
}
性能分析
O(logmod)
适用范围:mod是个素数的时候。
欧拉函数求逆元
原理
当a和p互素时
\(a^{\varphi(p)}\equiv 1 \pmod p\)
\(a^{\varphi(p)}*a\equiv 1 \pmod p\)
代码
//欧拉函数求逆元
int phi(int x)
{
int res=x,a=x;
for(int i=2;i*i<=a;i++)
{
if(a%i==0)
{
res=res/i*(i-1);
while(a%i==0)
a=a/i;
}
}
if(a>1)
res=res/a*(a-1);
return res;
}
int ksm(int a,int b=phi(mod)-1,int m=mod){
int res=1;
while(b){
if(b&1){
res*=a;
res%=m;
}
a*=a;
a%=m;
b>>=1;
}
return res;
}
性能分析
O(logmod)
适用范围:mod是个合数的时候。
递推求逆元
原理
\(inv[i]=(mod-mod/i)*inv[mod\)%\(i]\)%\(mod\)
inv[1]=1;
代码
int inv[mod+5];
void init(int mod){
inv[1]=1;
for(int i=2;i<mod;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
性能分析
时间复杂度O(n)
适用范围:mod数是不大的素数