poj 1061 青蛙的约会 扩展欧几里得定理

青蛙的约会

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 108538 Accepted: 21756

Description

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。

我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。

Input

输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行”Impossible”

Sample Input

1 2 3 4 5

Sample Output

4

题解:

首先我们要了解一下扩展欧几里德定理:
扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式: ax + by = gcd(a, b) = d(解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。

        对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得 gcd(a,b)= ax+by

        求解 x,y的方法的理解

设 a>b。
1,显然当 b = 0,gcd(a,b)= a。此时 x = 1,y = 0;
2,a > b > 0 时
设 ax+  by= gcd(a, b);
bx+ (a mod b)y=  gcd(b, a mod b);
根据朴素的欧几里德原理有 gcd(a, b) = gcd(b, a mod b);
则:ax1+ by1= bx2+ (a mod b)y2;
即:ax1+ by1= bx2+ (a – [a / b] * b)y2=ay2+ bx2– [a / b] * by2;
也就是ax1+ by1 == ay2+ b(x2– [a / b] *y2);
根据恒等定理得:x1=y2; y1=x2– [a / b] *y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b = 0,所以递归可以结束
int exGcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    int r=exGcd(b,a%b,x,y);
    int t=x;x=y;y=t-a/b*y;
    return r;
}
把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。
可以这样思考:
对于a’=b,b’=a%b 而言,我们求得 x, y使得 a’x+b’y=Gcd(a’,b’)
由于b’=a%b=a-a/b*b
那么可以得到:
a’x+b’y=Gcd(a’,b’) ===>
bx+(a – a / b * b)y = Gcd(a’, b’) = Gcd(a, b) ===>
ay +b(x – a / b*y) = Gcd(a, b)
因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b*y)
使用扩展欧几里德算法解决不定方程的办法
对于不定整数方程pa+qb=c,若 c mod Gcd(a, b)=0,则该方程存在整数解,否则不存在整数解。
有种较为不严谨的方法证明,不过至少弥补了一点空白,望某些数论大师补充修改:
由于我们知道,存在一组x与y使得a*x+b*y=gcd(a,b)。
将等式两边同时乘以整数k,即a*x*k+b*y*k=gcd(a,b)*k。如果c mod gcd(a,b)=f,则0<=f<gcd(a,b)。
那么可以令c=gcd(a,b)*k+f。这样一来,就有a*x*k+b*y*k+f=c。
若f

0,由于f<gcd(a,b)<=a<=b(假设a<=b),所以不存在f=a*m(m为整数),也就不存在a*(x*k+m)+b*y*k=c。也就是说,不存在a*x+b*y=c的整数解x与y。

所以f=0,即只有当c mod gcd(a,b)=0时,a*x+b*y=c有正整数解。得证。
上面已经列出找一个整数解的方法,在找到p * a+q * b = Gcd(a, b)的一组解p0,q0后,p * a+q * b = Gcd(a, b)的其他整数解满足:
p = p0 + b/Gcd(a, b) * t
q = q0 – a/Gcd(a, b) * t(其中t为任意整数)
至于pa+qb=c的整数解,只需将p * a+q * b = Gcd(a, b)的每个解乘上 c/Gcd(a, b) 即可,但是所得解并不是该方程的所有解,找其所有解的方法如下:
在找到p * a+q * b = Gcd(a, b)的一组解p0,q0后,可以
得到p * a+q * b = c的一组解p1 = p0*(c/Gcd(a,b)),q1 = q0*(c/Gcd(a,b)),p * a+q * b = c的其他整数解满足:
p = p1 + b/Gcd(a, b) * t
q = q1 – a/Gcd(a, b) * t(其中t为任意整数)
p 、q就是p * a+q * b = c的所有整数解。

扩展欧几里德算法不但能计算(a,b)的最大公约数,而且能计算a模b及b模a的乘法逆元。
在这里就不多说了。
 

那对于这道题来说:

说白了就是让你求这个(解a):

x+amy+an(modL)

它就等于

a(mn)yx(modL)

把模去掉,就等于

a(mn)+Lk=yx

然后,用exgcd求

a(mn)+Lk=gcd(mn,L)

d=gcd(m-n,L),c=y-x
c%d!=0,则无解。
这样解出a后,最终答案就是:

(ac/d)modL/d

关于最后一步的证明,我是这样想的:
设要解的方程(求x)是:

ax1+by1=c

而我们已经解得

ax+by=gcd(a,b)=d

此时将第二个方程左右同时乘c/d,则可得:

axc/d+byc/d=c

所以:

x1=xc/d

这样并没有完,因为这只是一组解,我们要求最小正整数解。
我们知道:若一组 < x,y > 是ax+by=c的一组解,那么

<xb/d,y+a/d>

也是原方程的一组解。
这样我们只需要让解得的x不断减b/d,直到再减就为负数时,所得的x就是我们要的解。
其实这个过程就是模运算,所以最小正整数解就是:

x1=(xc/d)modb/d

还有一种证法。对于这个式子:

ax1+by1=c

我们可以让等式两边同时除以d,则:

ax1/d+by1/d=c/d

相当于化简,此时对结果无影响,求就好了。
 
下面是青蛙的约会的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#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 t = exgcd(b, a%b, x, y);
	ll xx = x;
	x = y;
	y = xx - a/b*y;
	return t;
}
int main()
{
	ll m, n, x, y, mod;
	cin>>x>>y>>m>>n>>mod;
	if(m == n)
		puts("Impossible");
	else
	{
		if(m < n)
		{
			swap(x, y);
			swap(m, n);
		}
		ll s, k;
		ll c = y - x;
		ll d = exgcd(m-n, mod, s, k);
		if(c % d)
			puts("Impossible");
		else
			printf("%lld", ((s*c/d)%(mod/d)+(mod/d))%(mod/d));
	}
	return 0;
}

 
 

上一篇
下一篇