很直接的欧几里得算法题
为了叙述简明,我们不妨设输入数据为a,b,m,n,l
则很容易得到方程
[(m-n)*x+a-b] % l == 0
可改写为:
(m-n)*x-l*y == b-a
得到一个方程,根据扩展欧几里得算法解方程即可
最大公约数函数:
int gcd(int a, int b) {//证明省略
return b==0?a:gcd(b,a%b);
}
扩展欧几里得算法:
void expand_gcd(int a, int b, int d, int x, int y) {//其中d==gcd(a,b)
if(b == 0) {
d = a;
x = 1;
y = 0;
}
else {
expand_gcd(b, a%b, d, y, x);//注意这里参数顺序的变化
y -= x*(a/b);
}
}
这样不难解出方程(m-n)*x-l*y=gcd(m-n, l)的解(x0, y0),在(x0, y0)的基础上很容易解出原方程的解
代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 10010
#define LL long long
using namespace std;
LL gcd(LL a, LL b) {
return b==0 ? a : gcd(b, a%b);
}
void expand_gcd(LL a, LL b, LL d, LL &x, LL &y) {
if(!b) {
d = a;
x = 1;
y = 0;
}
else {
expand_gcd(b, a%b, d, y, x);
y -= x*(a/b);
}
}
int main(void) {
LL a, b, m, n, l, x, y;
LL c, d;
cin >> a >> b >> m >> n >> l;
if(m > n) {
c = b - a;
d = gcd(m-n, l);
expand_gcd(m-n, l, gcd(m-n, l), x, y);
}
else {
c = a - b;
d = gcd(n-m, l);
expand_gcd(n-m, l, gcd(n-m, l), x, y);
}
if(c % d) {
printf("Impossible\n");
}
else {
LL ans;
ans = (x*c/d)%(l/d);//这一步不太容易理解,见下面注释
if(x*c/d <= 0) {
ans += (l/d);
}
cout << ans << endl;
}
return 0;
}
ans = x*c/d很容易理解
因为x是原方程exp/c*gcd(m-n, l)的解
因此很容易得出原方程的一个解是ans = x*c/d;
但是为什么要mod(l/d)呢???
这是为了保证此时ans对应于两只青蛙第一次见面
设方程a*x+b*y=g//g=gcd(a,b);
令其一个解为(x0, y0)
则解空间可定义为(x0+k*b/g, y0-k*a/g) //k取任意整数
所以可知下一次见面为(x0+b/g, y0-a/g)
因此每两个相邻解x差值为b/g,因此要保证所求x对应青蛙第一次见面时间需令x%(b/g),若为负数则+b/g
代码中的l/d对应的就是b/g