《洛谷深入浅出进阶篇》 欧几里得算法,裴蜀定理,拓展欧几里得算法————洛谷P1516 青蛙的约会

本文章内容:

欧几里得算法:

gcd(a,b)=gcd(b,a%b)

由于篇幅问题,在这里就不加以证明,可以上b站自己搜一下。

由欧几里得算法我们可以很清楚的知道,a,b的最大公约数,等于b,a%b的最大公约数

裴蜀定理

对于任意一对整数a,b , 存在整数对(x,y) 使不定方程 ax+by= gcd(a,b)有解。

由裴蜀定理引出的定理:

若对于任意一对整数a,b , 存在整数对(x,y) 使不定方程 ax+by= c 有解,那么c必然为gcd(a,b)的倍数

证明很简单,由于x,y是整数,a是gcd 的倍数

,b也是gcd的倍数,所以c一定是gcd 的倍数

拓展欧几里得算法:

拓展欧几里得算法又被称为exgcd,其作用就是求上述的不定方程ax+by=c的所有解:

这里展开简单叙述,但不证明:

由欧几里得算法:
gcd(a,b)=gcd(b,a%b)

假设ax+by=gcd的解为 x1,y1。
由欧几里得+裴蜀定理我们不难得到另一种构造: bx2+(a%b)y2=gcd

这两个等式都等于c


也就是说我们有这样的等式:

ax1+by1 = bx2+(a%b)y2 

将a%b展开得到:


ax1+by1=    bx2+ (a-[a/b]*b)y2 =   bx2+ay2-b*[a/b]*y2 =    ay2+ b(x2-[a/b]*y2)

由恒等式定理:恒等式左边a的系数必然等于恒等式右边的a的系数

x1=y2
y1=x2-[a/b]*y2


由于我们已经知道a,b的值,倘若我们知道x2,y2的值,我们必然能够求解出x1,y1;

但是我们如何求解x2,y2呢,我们可以将 对其再用欧几里得+裴蜀定理进行构造。

也就是换元+重复上面的步骤,所以我们用递归解决。

然而递归的边界是什么呢?

我们不断进行 a=b,b=a%b的操作,最终b必然会为0,也就是 ax +0*y=gcd(a,b)

即原式最终可以推到成这个ax +0*y=gcd(a,b),或者也可以说从这个式子推导出原式。

那么此时 x=1,y=任何数,这里取0

所以我们就可以用 x=1,y=0 递归过去。

下面是代码实现:


void exgcd(int a,int b,int &x ,int &y){

   if(b==0){
       x=1,y=0;
       return;
   }

   exgcd(b,a%b,y,x);
 //不断递归,直到最次底层,由于每一层的x是上一层的y,每一层的y代表上一层的x,所以次底层的

//x1=0,y1=1,而次底层的x1为上底层的y2,次底层的y1为上一层的x2。即

//即x1=y2,y1=x2-a/b*y2

//所以:

y-=a/b*x

void exgcd(int a,int b,int &x,int &y){

if(b==0){
   x=1,y=0;
   return ;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}


}
  

由于此处的x,y是 ax+by=gcd的解,若要求ax+by=c的解,那么我们就直接给x,y乘 c/gcd 即可


然后我们利用拓展欧几里得算法求出了ax+by=c的一个解,


我们怎么求出所有的解?

只需要再次求出 ax+by=0的最小整数解即可。
(此处证明略)

a和b的最小公倍数lcm(a,b)

也就是ax=lcm(a,b) , by=-lcm(a,b)
即 : x= lcm(a,b)/a   ,  y= -lcm(a,b)/b

所以ax+by=c的所有解就是:(此处的k是任意整数,lcm是a,b的最大公倍数,由最大公倍数= 两数成绩/gcd,我们也可以将这个式子化简,自己动手即可)

x= x1+k*lcm(a,b)/a 
y= y1+k*lcm(a,b)/b

下面是关于P1516的题解:

x+mt == y+nt (mod L)

当他们同一时间,跳到同一点上:
假设这个数轴长L 米,经过t秒后

青蛙A应该在的坐标为: (x+mt)mod L
青蛙B应该在的坐标为: (y+nt)mod L

要使得最终能相遇,应该是在同一时间他俩的坐标相同,

也就是  x+mt  与 y+nt 同余 ,t有解整数解,求t的最小正整数解。

假如青蛙A 领先青蛙B  z圈,也就是青蛙A领先青蛙B,
zL米

则有:  x+mt + z*L = y+nt
 
(m-n)*t +L*z =y-x
也就是求,t,z的二元一次方程的解
由拓展欧几里得算法,
ax+by=gcd(a,b)


先判断 y-x是不是 gcd((m-n),L)的倍数
若不是,则直接输出不可能,结束函数。

若是,那么t,z一定有解,用拓展欧几里得求出他们的解

t1,z1.
注意,此时t1,z1的解是 :(m-n)*t +L*z =gcd(m-n,L)
的解,

因为我们要求的是:(m-n)*t +L*z =y-x,所以我们乘相应的倍数即可

所以真正的解 t1= t1* (y-x)/gcd(m-n,L)

然后再求出(m-n)*t +L*z =0 的最小整数解

解出 t2=L/gcd(m-n,L),z2=-(m-n)/gcd(m-n,L)

假设gcd(m-n,L)=d
然后就可以得到所有的解:
t=t1+k*L/d;


然后如果当前得到的解,小于0,只要不断的加上L/d,直到其大于0即可

若得到的解大于零,只要mod L/d 就可得到最小正整数解

上代码:

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) {
	if (b == 0) {
		x = 1, y = 0;return a;
	}
	LL d=exgcd(b, a%b, y, x);
	y -= (a / b) * x;
	return d;
}
int main() {
	LL x, y, m, n, L,z,t;
	cin >> x >> y >> m >> n >> L;  //  x+t*m +z*L == y+t*n
	LL d=exgcd(m - n, L, t, z);  //  变成不定方程 (m-n)*t+L*z == y-x ,// 先求出 exgcd(m-n,L,t,z) 
	if ((y - x) % d != 0) { cout << "Impossible"; return 0; }// 若t,z有解,说明 y-x 是 gcd(m-n,L)的倍数
	t = t * (y - x) / d;// 那么 t1 = 扩展欧几里得得到的t2 * (y-x)/gcd
	LL t3 = L / d;//再求出   t3*(m-n) + z3*L=0 的最小整数解,也就是 最小公倍数除以系数,// t3 =  L / gcd
	//得到通式:t=t1+ t3*k
	if (t < 0) {
		for (int i = 0;t<0; i++) {
			t += t3 * i;
			if (t >= 0)break;
		}
		cout << t % t3;
	}
	else if (t > 0) 
		cout << t % t3;
}

你可能感兴趣的:(洛谷深入浅出进阶篇,算法,数论,c++,gcd,拓展欧几里得,洛谷深入浅出进阶篇)