gcd+exgcd+例题(青蛙的约定)
1.gcd的两种求法:
一、更相减损法
两个正整数$a$和$b$($a>b$),它们的最大公约数等于$a-b$的差值$c$和较小数$b$的最大公约数。
1 int gcd(int a,int b)
2 {
3 if(a==b)
4 return a;
5 if(a>b)
6 return gcd(a-b,b);
7 if(a<b)
8 return gcd(b-a,a);
9 }
二、辗转相除法
两个正整数$a$和$b$($a>b$),它们的最大公约数等于$a$除以$b$的余数$c$和$b$之间的最大公约数。
1 int gcd(int a,int b)
2 {
3 if(a%b==0)
4 return b;
5 else
6 return gcd(a%b,b);
7 }
2.exgcd
裴蜀定理:对于任意的整数$a$,$b$,都存在一对整数$x$,$y$,使得$ax+by=gcd(a,b)$成立。
可以看出,这是一个递归求解的函数。在函数递归到最后的时候,存在$b=0$,不管$a$是什么,这时显然有一对整数$x=1$,$y=0$来使得:
$a×1+0×0=gcd(a,0)$
那么,我们通过这个递归的实现过程来进行回溯的模拟。当$b>0$ ,则程序还可以继续往下走:$gcd(b,a%b)=gcd(a,b)$。这时假设存在一对整数$x,y$ ,使得其一定会满足$b×x+(a%b)×y=gcd(b,a%b)$, 因为$a%b=a−b⌊a/b⌋ $,所以有以下的推导:$b×x+(a%b)×y=gcd(b,a%b)=bx+(a−b⌊a/b⌋)y=ay−b(x−⌊a/b⌋y)$这个时候令$x′=y,y′=x−⌊a/b⌋$,再结合一开始的原式子,就得出:$ax′+by′=gcd(a,b)$
*裴蜀定理的应用:
$ax+by=exgcd(a,b)$
那么可以推出:如果一个数$m$满足:$ax+by=m$,那么这个$m$一定是$gcd(a,b)$的倍数。
那么对于一个经典方程$ax+by=1$,利用裴蜀定理,我们有:$gcd(a,b)=1$,即$a,b$一定互质。
扩展欧几里得算法
在介绍扩展欧几里得算法之前,我想首先介绍它的应用:
1、求解不定方程
2、求解模的逆元
3、求解线性同余方程
为什么它能应用到这几个方面呢?回到裴蜀定理:$ax+by=m$
对于这个不定方程,如果存在一组合法的解$(x,y)$,那么一定会有$gcd(a,b)|m$,即$m$是$gcd(a,b)$的倍数。那么现在我不仅想知道到底有没有解,而是想知道在有解的情况下,这个解到底是多少。
1 void exgcd(int a,int b,int &x,int &y){ 2 if (!b){x=1;y=0;return;} 3 exgcd(b,a%b,y,x); 4 y-=(a/b)*x; 5 return; 6 }
*青蛙的约定
• 设$x_0$为最终结果,让青蛙相对运动,可列
出式子:
• $(m - n)x_0 = y - x (mod L)$
• 显然该式可以转化为:
• $(m - n)x_0 + Ly = y - x$
• 再变换一下变成:
• $ax + by = c$
• 变成了熟悉的$exgcd$问题,正常求解即可。
1 #include<iostream>
2 #include<cstdio>
3 #include <algorithm>
4 #define ll long long
5 using namespace std;
6 ll ans,x1,y1;
7 ll exgcd(ll a,ll b,ll &x1, ll &y1)
8 {
9 if(!b)
10 {
11 x1=1;
12 y1=0;
13 return a;
14 }
15 ans=exgcd(b,a%b,x1,y1);
16 ll t=x1;
17 x1=y1;
18 y1=t-a/b*y1;
19 return ans;
20 }
21
22 int main()
23 {
24 ll n,m,x,y,l;
25 cin>>x>>y>>m>>n>>l;
26 ll b=n-m,a=x-y;
27 if(b<0)
28 {
29 b=-b;
30 a=-a;
31 }
32 exgcd(b,l,x1,y1);
33 if(a%ans!=0)
34 cout<<"Impossible";
35 else
36 cout<<((x1*(a/ans))%(l/ans)+(l/ans))%(l/ans);
37 }