poj 1061 青蛙的约会 数论

来源:互联网 发布:g76螺纹编程第一刀 编辑:程序博客网 时间:2024/05/19 22:58

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

#include<stdio.h>#include<string.h>int gcd(__int64  a,__int64  b){    return b==0? a:gcd(b,a%b);}void extend_gcd (__int64  a , __int64  b ,__int64  &x , __int64  &y){    if(!b)    {        x = 1;        y = 0;    }    else    {        extend_gcd(b, a%b, y, x);        y-=x*(a/b);    }}int main(){    __int64 x,y,m,n,xx,l,g,a,b,c;    while(scanf("%I64d %I64d %I64d %I64d %I64d",&x,&y,&m,&n,&l)!=EOF)    {        a=n-m;        b=l;        c=x-y;        g=gcd(a,b);        if(c%g)            printf("Impossible\n");        else        {            extend_gcd(a,b,x,y);            xx=x*(c/g);            xx=(xx%b+b)%b;            printf("%I64d\n",xx);        }    }    return 0;}



要做此题,先要明白几个定理,为保严谨性我按顺序一一证明出来(心病,不证明的话用得不踏实啊!不然我就不用跑来GDCC了!)

唉,证明太枯燥无味了,先说一下题意吧。

公青蛙一开始在x位置,母青蛙在y位置。公青蛙每次跳m米,母青蛙每次跳n米,并且都是向右跳的。地球经线长度是L,然后地球是圆的,也就是说,跳到L、L+1、L+2……其实就是跳到0、1、2。 公青蛙想追母青蛙,问多少次后它们能跳到一起。如果它们永远不能相遇,就输出Impossible(好可怜啊!) 

很明显嘛,就是求一个k,使x + k*m ≡ y + k*n (mod L) 嘛,木有错吧?至少我是这么想滴!然后对方程化简咯,就变成(n-m) * k ≡ x-y (mod L)咯。然后这个方程其实就等价于(n-m)*k + L*s = x-y咯。这就是ax + by = c求整数x的模型。

要求ax + by = c的整数x解,可不是那么简单滴,不然我就不用花了一整个晚上时间了!首先,设d = gcd(a, b),方程两边除以d得到a/d * x + b/d * y = c/d,很显然嘛,a是整除d的,b也是整除d的,而x、y都是整数解,所以要求c/d也是整数嘛。如果c不整除d,当然就是Impossible咯。不然的话,如果我们能求出ax0+by0=d的解x0和y0,那么两边乘以c/d即a(c/d * x0) + b(c/d * y0) = c,就可以得到原来方程的解x = (c/d * x0),y = (c/d * y0)咯。

喂喂喂,等一下,你又怎么知道ax0 + by0 = d一定有解呢?这就得通过严谨的证明了:

定理一:如果d = gcd(a, b),则必能找到正的或负的整数k和l,使d = a*k + b*l。

证明由于 gcd(a, 0) = a,我们可假设b ≠ 0,这样通过连除我们能够写出

a = b*q1 + r1

b = r1*q2 + r2

r1 = r2*q3 + r3

……

 由第一式有r1 = a - q1*b,所以r1能写成k1*a + l1*b的形式(这时k1 = 1, l1 = -q1)。由第二式有r2 = b - r1*q2 = b - (k1*a + l1*b)*q2 = -q2*k1*a + (1 - q2*l1)*b = k2*a + l2*b。

显然,这过程通过这一串余数可重复下去,直到得到一个表达式rn = k*a + l*b,也就是d = k*a + l*b,这就是我们所要证明的。

 

所以说咯,ax0 + by0 = d一定有解!那么应该怎么求x0和y0呢?要用到一种叫做扩展欧基里得的算法(NND,第一次听说,我还是太弱了啊~~~)!

求法如下:由于gcd(a, b) = gcd(b, a%b) (这个不用证明了吧???地球人都知道!),有ax0 + by0 = gcd(a, b) = gcd(b, a%b) = bx1 + (a%b)y1,而a%b又可以写成a-a/b*b(a/b*b不等于a啊!记得为了这个SB问题还被骂过~),所以=bx1 + (a-a/b*b)y1 = ay1 + b(x1-a/b*y1),所以如果我们求出gcd(b, a%b) = bx1 + (a%b)y1的x1和y1,那么通过观察就可以求出x0 = y1,y0 = (x1 - a/b*y1)。那我们怎样求x1和y1呢?当然是求x2和y2了,做法一样滴。一直求到gcd(an, 0) = an*xn + 0 * yn,这时令xn=1,yn=0就完事了,就可以求xn-1和yn-1,然后xn-2和yn-2,然后一直求到x0和y0了。

所以得到ax0 + by0 = gcd(a, b)求整数x0、y0的扩展欧基里得算法


0 0
原创粉丝点击