首先说下逆元的意思
若存在ax≡1(modax≡1(mod p)p),那么可以成 x 为 a 的逆
模 p 意义下,一个数 a 如果有逆元 x ,那么除以 a 相当于乘以 x
那么逆元可以用来做什么呢
有的题目中想要求abab模MM,但是..分数模意义就有了不同,那么就可以通过逆元来计算
下面说几种求逆元的方法
1. 扩展欧几里得
ax≡1(modax≡1(mod p)p) 相当于ax=1+pyax=1+py,也就是ax−py=1(modax−py=1(mod p)p)
注:这种算法要求 gcd(a,p)=1,否则则没有逆元
例如 hdu1576
#include <cstdio>
#define M 9973
int exgcd(int a,int b,int &x,int &y){
if(!b){x=1,y=0;return a;}
int c=exgcd(b,a%b,y,x);
y-=a/b*x;
return c;
}
int main(){
int T,a,b,x,y;
scanf("%d",&T);
while(T--){
scanf("%d%d",&a,&b);
exgcd(b,M,x,y);
x=(x*a%M+M)%M;
printf("%d\n",x);
}
return 0;
}
2.费马小定理
费马小定理是说 ap−1≡1(modap−1≡1(mod p)p) (p(p为素数,gcd(a,p)=1)gcd(a,p)=1)
因此 a×ap−2≡1(moda×ap−2≡1(mod p)p)
ap−2ap−2也就是a得逆元
求ap−2ap−2就可以用快速幂等方法啦
例如 hdu1576
#include <cstdio>
#define M 9973
int T,a,b;
int pow(int x,int y){
int t=1;
while(y){
if(y&1) t=t*x%M;
x=x*x%M;y>>=1;
}return t;
}
int main(){
freopen("hdu1576.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d%d",&a,&b);
b=b%M;
printf("%d\n",a*pow(b,M-2)%M);
}
return 0;
}
3.逆元打表(递推)
我们定义inv[i]inv[i]为模MM(M为质数) 意义下的逆元,那么inv[i]=(M−Mi)×inv[Minv[i]=(M−Mi)×inv[M%i]i]%MM
怎么证明呢
我们假设
那么
左右同除i∗ki∗k
因此
代码如下
inv[1] = 1;
for(i=2;i<M;++i) inv[i]=(M-M/i)*inv[M%i]%M;
4.欧拉定理
欧拉定理是说:若 a 与 n 互质,则aϕ(n)≡1(modaϕ(n)≡1(mod n)n)
由此可以看出..欧拉定理相当于费马小定理的推广吧(因为ϕ(n)=n−1ϕ(n)=n−1嘛)
ϕ(n)ϕ(n)就是欧拉函数,表示不超过n,且与n互质的正整数的个数
例如:
n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
ϕ(n)ϕ(n) | 1 | 1 | 2 | 2 | 4 | 2 | 6 | 4 | 6 | 4 |
有两个引理
- 引理1
- (1)如果n为素数,则ϕ(n)=n−1ϕ(n)=n−1
- (2)如果n为素数p的m次方,则ϕ(n)=ϕ(pm)=(p−1)×pm−1ϕ(n)=ϕ(pm)=(p−1)×pm−1
- (3)如果n为任意两个互质的数a、b的积,则ϕ(n)=ϕ(a×b)=ϕ(a)×ϕ(b)ϕ(n)=ϕ(a×b)=ϕ(a)×ϕ(b)
- 引理2
- n=pa11×pa22×pa22⋯×pakkn=p1a1×p2a2×p2a2⋯×pkak(其中pipi为质数),则ϕ(n)=n×(1−1p1)×(1−1p2)⋯×(1−1pk)ϕ(n)=n×(1−1p1)×(1−1p2)⋯×(1−1pk)
当然还要许多结论qaq,这里就先说一个跟求欧拉函数有关的吧~
若p为素数,p为x约数,则
若p不为x约数,则
证明在这里就先不说了…(有时间再填坑吧~)
例题:hdu2824
求一段区间欧拉函数值
#include <cstdio>
#include <cstring>
#define N 3300000
int prime[N],isprime[N],phi[N],tot=0,a,b;
void euler(){
memset(isprime,true,sizeof(prime));isprime[0]=isprime[1]=false;
for(int i=2;i<=3000000;i++){
if(isprime[i]) prime[++tot]=i,phi[i]=i-1;
for(int j=1;i*prime[j]<=3000000;j++){
int k=i*prime[j];
isprime[k]=false;
if(i%prime[j]==0){
phi[k]=phi[i]*prime[j];
break;
}else phi[k]=phi[i]*(prime[j]-1);
}
}
}
int main(){
euler();
while(scanf("%d%d",&a,&b)>0){
long long ans=0;
for(int i=a;i<=b;i++) ans+=(long long)phi[i];
printf("%lld\n",ans);
}
return 0;
}
5.特殊情况
条件b|a
那么
这个看起来就很有趣了~ 不过..我还没有试过【捂脸】