POJ - 1061 青蛙的约会——扩展欧几里得

一年前刷的题,然而一年没刷数学了。。。。。。

题意:

A开始在p点,每次可以前进m,B开始在q点,每次可以前进n,坐标轴是一个长度为L的环,问A前进多少次后可以遇到B

思路:

设一个可行的前进次数为x,那么可以列出方程:

(p+x*m)%L = (q+x*n)%L

转化一下:

(x*(m-n))%L = (q - p)%L

设(x*(m-n))与(q-p)之间相差y个L

则:

(x*(m-n)) + y * L = q - p;

这里注意一下如果m-n是负数的话要换成n-m,相应的q-p换成p-q(只保证m-n非负就可以,不用保证q-p非负)

设a=(m-n), b = L, c = q - p;

那么(x*(m-n)) + y * L = q - p就变成了a*x + b*y  = c

根据扩展欧几里得定理我们可以求得a*xx + b * yy = gcd(a, b)的解xx,那么x=xx*c/gcd(a, b)

但这只是x的任一解,题目中要求的是求x的最小正整数解

我们先把a*xx + b * yy = gcd(a, b)求出的解(xx,yy)带入a*x + b*y  = c得:

(a*c/gcd(a,b))*x+(b*c/gcd(a,b))*y = c

那么x每减少tx,y就要增加ty,保证(a*c/gcd(a,b))*tx=(b*c/gcd(a,b))*ty,移项得tx/ty = (b/gcd(a, b)) / (a/gcd(a, b))

我们可以推断出要想让x最小,就要尽可能多的去除tx,也就是尽可能多的去除(b/gcd(a,b)), 答案就是x%(b/gcd(a, b))

考虑到x一开始就是负数的情况,把式子转化一下:(x%(b/gcd(a, b))+(b/gcd(a, b))) % (b/gcd(a, b)),就是最终答案了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    ll r = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return r;
}
int main() {
    ll p, q, m, n, L;
    while (~scanf("%lld%lld%lld%lld%lld", &p,&q,&m,&n,&L)) {
        ll a = m - n, b = L, c = q - p, x, y;
        if (a < 0) a = -a, c = -c;
        ll d = exgcd(a, b, x, y);
        if (c % d) {
            printf("Impossible\n");
            continue;
        }
        x *= c / d;
        ll t = b / d;
        printf("%lld\n", (x % t + t) % t);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/hao_zong_yin/article/details/80271195