POJ 1061(扩展欧几里得)

初学,这是第一道题,略水。

首先还是自己理一理思路说一说扩展欧几里得。

欧几里得就不说了,相信大家都已经记住了,那什么又是扩展欧几里得呢?

我们知道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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值