一、不定方程推导
\(a\):青蛙\(a\)的起点
\(b\):青蛙\(b\)的起点
\(m\):青蛙\(a\)一次能跳多远
\(n\):青蛙\(b\)一次能跳多远
\(L\):一圈的距离
\((b-a)\):\(a\)要追\(b\)多少米(这个数可能是正的,也可能是负的。正的表示\(b\)在\(a\)前面;如果是负的表示\(b\)在\(a\)后面)
\((m-n)\):每跳一次,\(a\)能追\(b\)多少米
\(x\)是总共跳了多少次
\(y\)是\(a\)追\(b\)不一定会在一圈内追完,而是追了\(y\)圈
\((m-n)\):常数, \(L\):常数, \(b-a\):常数,变量:\(x,y\)
二、是否有整数解
如果想要有整数解,那么需要满足裴蜀定理: \(gcd(m-n,-L) | b-a\) ,否则无解
三、最小正整数解求解过程
1 、求\(ax+by=gcd(a,b)\)的一组特解
利用扩展欧几里得,计算出 \(ax+by=gcd(a,b)\)的一组特解 \(x_0,y_0\)。
2、求\(ax+by=c\)的一组特解
我们要求的方程通用形式是 \(ax+by=c\),并不是 \(ax+by=gcd(a,b)\),但满足 \(gcd(a,b) |c\)
也就是 \(c\) 肯定是 \(gcd(a,b)\)的整数倍。可以直接在 \(ax_0+by_0=gcd(a,b)\)这个等式的两端,同时乘以 \(t=c/gcd(a,b)\)
得到: \(ax_0 * t + by_0 * t =c\)
换句话说, \(x_0*t\)与 \(y_0*t\)就是方程 \(ax+by=c\) 的一组特解!
此时 \(x_1= x_0 * c /gcd(a,b) \ \ \ \ y_1= y_0 * c /gcd(a,b)\)
3、求\(ax+by=c\)的通解
这个\(x_1,y_1\)只是方程\(ax+by=c\)的一组特解,不一定是最小正整数解,也可能是负的,也可能很大的正数,需要通过通解公式,
计算出最小的正整数解:
通解公式: $$x= x_1+ k(b/d)$$ $$y=y_1-k(a/d)$$,其中\((b/d)\)可以理解为 最小缩放单元 。
为了防止出现负数,一般采用类似下面的取绝对值办法,整理为正整数单元。
LL t = abs(b / d);
在当前题目中就是: \(x = x1+ k(-L/d)\) 其中\(k\)是任意整数
现在 \(x1,L/d\)都是已知的常数,\(k\)可以变化 ,求\(x\)的最小正整数解是多少。
\(x\)其实本质是就是通过 \(x_1\)在扩大、缩小 \(L/d\) \(k\)倍得到,
比如\(k=1\),扩大 \(x=x_1+ L/d\);
比如\(k=2\) 扩大 \(x=x_1+ 2*(L/d)\)
当然,也可以缩小
比如\(k=-1\) 缩小 \(x=x_1-(L/d)\)
比如\(k=-2\) 缩小 \(x=x_1-2*(L/d)\)
不管怎么样,都最后要满足\(x\)是最小的正整数。
我们通过循环变化, 就可以得到\(x\)的最小正整数解:
//方法II:循环尝试法
LL ans = x1 % t;
while (ans <= 0) ans += t;
cout << ans << endl;
当然,也可以直接使用证明过的公式来计算:
cout << (x % t + t) % t << endl; //求最少跳几次后碰面
四、实现代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
LL a, b, m, n, L;
cin >> a >> b >> m >> n >> L;
LL x, y;
LL d = exgcd(m - n, -L, x, y);
if ((b - a) % d)
puts("Impossible");
else {
x *= (b - a) / d;
LL t = abs(L / d);
//求最小正整数解
//方法I:公式法
cout << (x % t + t) % t << endl; //求最少跳几次后碰面
//方法II:循环尝试法
// LL ans = x1 % t;
// while (ans <= 0) ans += t;
// cout << ans << endl;
}
return 0;
}
五、实现代码II
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
LL a, b, m, n, L;
cin >> a >> b >> m >> n >> L;
//让快的追慢的,如果不是的话,掉过来~,这样做后,可以保证m-n>0,最大公约数是正数d>0
if (m < n) swap(m, n), swap(a, b);
LL x, y;
LL d = exgcd(m - n, L, x, y);
if ((b - a) % d)
puts("Impossible");
else {
x *= (b - a) / d;
LL t = L / d;
//求最小正整数解
//方法I:公式法
cout << (x % t + t) % t << endl; //求最少跳几次后碰面
//方法II:循环尝试法
// LL ans = x1 % t;
// while (ans <= 0) ans += t;
// cout << ans << endl;
}
return 0;
}