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

思路:设青蛙跳了k次,那么就有(x+mk)-(y+nk)=p*L.

即x-y+(m-n)k=p*L,即(m-n)*k≡(y-x) (mod L).这个线性同余方程有解当且仅当gcd(m-n,L)|(y-x).

令a=m-n,b=L,c=y-x.用扩展欧几里得解方程ax+by=c.可以求出原方程的一个解.如何求最小正整数解呢?假设我们已经得到一个x0,令d=gcd(m-n,L),那么所有解可以表示为x=x0+k*L/d.设L'=L/d.Xmin=(x0 mod L'+L') mod L'.

推导:若最终要求的方程为:
ax + by = c = k*gcd(a, b)
并设方程
ax1 + by1 = gcd(a, b) 已有解(x1, y1)
两边同时乘上k:
akx1 + bky1 = k*gcd(a, b)
左边作恒等变换
akx1 + bky1 = k*gcd(a, b) <=>
akx1 + bky1 + k1*lcm(a, b) - k1*lcm(a, b) = k*gcd(a, b) <=>
a(kx1 + k1b/gcd(a, b)) + b(ky1 - k1a/gcd(a, b)) = c ...(5)
其中k1为任意整数,lcm为最小公倍数
比较(4) (5)方程可得
x = kx1 + k1b/gcd(a, b)
y = ky1 - k1a/gcd(a, b)
即为最终解。

扩展欧几里得算法:http://www.acmerblog.com/extend-gcd-5610.html

#include <cstdio>
#include <cstring>
#define ll long long

using namespace std;

ll extend_gcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    else
    {
        ll r = extend_gcd(b, a%b, y, x);
        y -= x*(a/b);
        return r;
    }
}

int main()
{
    ll x, y, m, n, l;
    while(~scanf("%I64d%I64d%I64d%I64d%I64d", &x, &y, &m, &n, &l))
    {
        ll t, p;
        ll ans = extend_gcd(n-m, l, t, p);
        if((x-y) % ans)
            printf("Impossible\n");
        else
        {
            //求最小整数解的算法
            t = (x-y)*t/ans;                        //首先令x为一个特解
            t = (t % (l/ans)+(l/ans)) % (l/ans);    //再根据公式计算
            printf("%I64d\n", t);
        }
    }
}

你可能感兴趣的:(poj 1061 青蛙的约会(扩展欧几里得算法))