扩展欧几里得
在讲欧几里得之前我想写说gcd,因为在我学习exgcd时候没有深刻理解递归gcd导致看了很长时间的exgcd的都代码才懂
辗转相除
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
在辗转相除的应用中我们可以知道的是gcd(a,b)=gcd(b,a%b);然后层层递归求得当b=0时候的解
贝祖定理
当a,b∈Z,那么一定存在x,y∈Z使得ax+by=gcd(a,b);
现在给出一个方程ax+by=m,如果此方程有解,那么m一定是gcd的若干倍(用来判断这样的式子是否有解)
特例:
ax+by=1,此时gcd=1
扩展欧几里得
板子:
int exgcd(int a,int b,int &x,int &y)
{
if(b==0){
x=1,y=0;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
要注意的是这个板子是简化版直接替换了x,y的位置
完整版:
int exgcd(int a,int b,int &x,int &y)
{
if(b==0){
x=1,y=0;
return a;
}
int r=exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return r;
}
逆元
为什么要求逆元?
因为除法求余是错误的!- (a÷b)%c=(a%c÷b%c)%*c*
exgcd求逆元
int exgcd(int a,int b,int &x,int &y)
{
if(b==0){
x=1,y=0;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
要注意的是求出来的逆元可能是‘负值’,还有后面的处理
ll x=0,y=0;exgcd(b,mod,x,y);
if(x<0) x+=mod;
ll ans=(n*((x%mod+mod)%mod))%mod;
这里的x就是a%b的逆元,y是b%a的逆元
注意
要理解引用的用法
费马小定理
快速幂求逆元-求a的p-2次方
前提:
gcd(a,p)=1且p为质数--具体不清楚以后再来补这个坑
递推求阶乘逆元-未理解直接抄的
我们经常会用到阶乘的逆元,我们可以考虑用费马小定理先求出最大那个阶乘的逆元,然后再往回推,直接看代码再解释。
void init() {
fact[0] = 1;
for (int i = 1; i < maxn; i++) {
fact[i] = fact[i - 1] * i %mod;
}
inv[maxn - 1] = power(fact[maxn - 1], mod - 2);
for (int i = maxn - 2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) %mod;
}
}