gcd——最大公约数
基本求法为辗转相除(欧几里得算法),有一些优化,网上搜得到就不详细说明了。
辗转相除(x>=y):gcd(x,y)=gcd(y,x%y) 当y=0时,答案为x
代码如下:
inline int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
代码极其简短….
下面是exgcd(扩展欧几里得算法),用来装逼处理ax+by=c=gcd(a,b)*k的同余方程
易证 当c%gcd(a,b)不等于0时,该方程无整数解。反之,则有无数整数解
扩欧的思想就是把欧几里得算法在方程中不断转换,并利用恒等定律,递归求出x,y的一组解
下面是一步递归的演算过程:
①ax1+by1=gcd(a,b)
②bx2+(a%b)y2=gcd(b,a%b)
∴ax1+by1=bx2+(a%b)y2
ax1+by1=bx2+(a-(a/b)」*b)y2;
ax1+by1=bx2-b*(a/b)」y2+ay2
ax1+by1=ay2+b(x2-y2*(a/b)」)
∴x1=y2,y1=x2-y2*(a/b)」
那么这个过程就可以在gcd过程中完成,在gcd的过程中完成对x和y的赋值
结果在什么地方呢?
那么根据gcd函数的结果,最后b一定会等于0。
当b=0时,gcd(a,0)=a原方程也就变成了ax=a 显然。。。此时x=1。
那么久可以滚回溯了
代码也是极其短小精悍(我才没压行
inline void exgcd(LL a,LL b,LL &x,LL &y){
if (!b) x=1,y=0;
else exgcd(b,a%b,y,x),y=y-a/b*x;
}
下面附vijos1009 exgcd的程序 可以参考 爱抄不抄
#include<iostream>
#include<cstring>
#include<cstdio>
#define LL long long
using namespace std;
LL x,y,m,n,L,a,b,c;
inline LL gcd(LL a,LL b){
if (!b) return a;
else return gcd(b,a%b);
}
inline void exgcd(LL a,LL b,LL &x,LL &y){
if (!b) x=1,y=0;
else exgcd(b,a%b,y,x),y=y-a/b*x;
}
int main(){
scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L);
a=n-m; b=L; c=x-y;
if(a<0)
{
a=-a;c=-c;
}
LL t=gcd(a,b);
if (c%t){
printf("Impossible\n");
return 0;
}
a/=t; b/=t; c/=t;
x=y=0;
exgcd(a,b,x,y);
x=((c*x)%b+b)%b;
if (!x) x+=b;
printf("%lld\n",x);
}
补:exgcd求逆元
今天上课突然讲到逆元。。想起来这里还有个坑没填- -
a*b≡1(mod p)则称a,b在p的模域下互为逆元
当p=∞,a=1/b
但是如果p是有限的,那么就不是那么简单了
欧拉函数求逆元和。。a^p-2次方我这里就不说了,毕竟主题是exgcd
上面讲过exgcd求的是:“Ax+By=gcd(A,B)”这样一个式子,然而显然只有在上述的a和p互质的情况下才存在逆元
所以gcd(a,b)=1,很容易理解
a*b≡1(mod p),我们可以转化为a*b-p*L=1
即a*b+p*(-L)=1
那么另b=x,p=B,a=A,(-L)=y;
则原式=A*x+B*y=1=gcd(A,B)
那么这个式子就可以用exgcd求解了
exgcd(a,p,x,y)
得到的x,即为a在p模域下的乘法逆元
——end