初学,这是第一道题,略水。
首先还是自己理一理思路说一说扩展欧几里得。
欧几里得就不说了,相信大家都已经记住了,那什么又是扩展欧几里得呢?
我们知道ax+by=c是有很多组解的,那么我们先令t = c / gcd(a, b);
那么,c/t=gcd(a,b),x/t, y/t,我们可以得到ax+by=gcd(a,b),此时x与y的值已经变化。
我们此时已经得到了ax+by=gcd(a,b)这样一个方程,那么我们可以求得这个方程的一个特解x0、y0,而这个特解则能够表示这个方程的所有解。
x = x0 + b / gcd(a,b) * z;
y = y0 - a / gcd(a,b) * z; (其中z为次数....应该都懂吧)
为什么要这样列呢?a、b除以他们的gcd则互质,那么上面的两个表达式尽可能多的利用特解表示了所有的解
(我们可以把x、y这两个表达式代入原方程,可以发现会变成ax0+by0=gcd(a,b)这样的形式,这也就反证了x、y两个表达式只是穷举了方程实现的一般解,并没有对方程进行改变)
那么我们来看看,可以以何种漂亮的姿势来求得这个特解x0、y0。
我们在这里要借助于扩展欧几里得算法,我们先来看看代码
void exgcd(LL a, LL b)
{
if (b == 0)
{
x = 1;
y = 0;
return ;
}
exgcd(b, a % b);
LL temp = x;
x = y;
y = temp - a / b * y;
}
LL代表long long,x,y我设置的全局变量....虽然不是什么好习惯但是.....习惯就好O(∩_∩)O
后面三行我们先不看,那么这个函数则与欧几里得算法近似。
我们先来看一个东西
ax+by=gcd(a,b)-->bx+a%by=gcd(a,b)
这个为什么会成立呢?
gcd(a,b) == gcd(b,a%b)
既然ax+by=gcd(a,b) ①,那么它可以转化成ax+by=c
既然bx+a%by=gcd(a,b) ②,那么它是不是也可以转化成ax+by=c,那么它俩是不是相等呢?
又来看
a % b = a - a / b * b ③
我们把③式带入②式
化简之后可以得到
ay+b(x-a/b*y) = gcd(a,b)
我们令①式中的xy分别为x0,y0,那么我们可以得到一个关系式
x0 = y ④
y0 = x - a / b * y ⑤
此时我们回过头来看扩展欧几里得的后三行,我们可以发现这是一个不断回溯的写法,函数的终止条件为b == 0。
注意,此时的函数实际上变为了ax+0*y = gcd(a,b),学过欧几里得算法的都知道,此时的a已经是gcd(a,b)了,那么此时只有x=1才满足关系方程。
那么我们终止递归进行回溯,我们根据④⑤式不断回溯,从最低回溯直到找到特解x0、y0。
至此,扩展欧几里得已经进行完毕,但是得到的特解x0y0或许并不满足你这个题目的答案(ACM的题总是很恶心的TAT)
经常我们仍然需要对这组解进行处理,比如得到满足条件的最小解之类的=。=
(由于蒟蒻初学....还没有写那么多的题,所以各种求解姿势会在之后补充,希望各位监督QAQ)
————————————————————————————分割线——————————————————————————
题目是中文,就不赘述了、、、
根据题意,令跳跃次数为t,绕赤道的圈数为k,我们可以列出这样一个方程
(x+mt) - (y+nt) = kL;
通过化简,我们可以得到如下的方程形式
(n-m)t + kL = x-y;
是不是感觉有了一点ax+by=c的雏形了
那么我们令a = n - m, b = L, c = x - y.
我们就能真正得到ax+by=c了。
代码如下:
#include <iostream>
#include <cmath>
#include <cstdio>
#define LL long long
using namespace std;
LL t, k;
LL gcd(LL a, LL b)
{
if (b == 0)
return a;
else
return gcd(b, a% b);
}
void exgcd(LL a, LL b)
{
if (b == 0)
{
t = 1;
k = 0;
return ;
}
exgcd(b, a % b);
LL temp = t;
t = k;
k = temp - a / b * k;
}
int main()
{
LL x, y, m, n, L, num;
while (scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L) != EOF)
{
LL a = n - m;
LL b = L;
LL c = x - y;
LL test = gcd(a, b);
if (c % test != 0)
cout << "Impossible" << endl;
else
{
num = c / test;
exgcd(a, b);
t *= num;
b /= test;
t = t % b;
t < 0 ? printf("%lld\n", t + b) : printf("%lld\n", t);
}
}
return 0;
}
但是结尾我们需要处理一点东西,我们讲c变为gcd(a,b)的时候,将c缩小了c/gcd(a,b)倍,我们需要还原一下求得的t,不还原的话可能会出现得到的t满足gcd(a,b)但是不满足c的情况,但是还原后求得的t可能并不是最小解了。
之前我们说过,x = x0 + b / gcd(a,b) * z
也就是说,当ab互质的时候也就是a/gcd(a,b),b/gcd(a,b)时,可以得到最小的且满足方程的a、b值
通过观察上式我们可以发现,x的最小解一定是存在于x0%b里面的。
关于证明,我们可以假设最小解为m,那么上式可以转化为
x = m + b * z (注意此时的b已经是最小的质数解了),那么这个表达式也可以表达方程所有的解。
而且此时的m的值是蛙跳步数最小的解,至此,答案已出。
另外,这道题数据过大得用long long哦O(∩_∩)O