本文章内容:
欧几里得算法:
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<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<cctype>
#include<map>
#include<set>
#include<queue>
#include<numeric>
#include<iomanip>
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;
}