poj 1061青蛙的约会 (数论:扩展的欧几里得算法)

很直接的欧几里得算法题

为了叙述简明,我们不妨设输入数据为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



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值