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

青蛙的约会
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 67617 Accepted: 10832

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"

===================================================================

感觉下面的分析写得很好。转自:http://qa4862741.blog.163.com/blog/static/56587549201112765242423/
下面说下具体思路(经网上整理并给出详细过程,有些解题报告是直接给出结论):

1).我们先这样考虑,A跳了t步的坐标为x+mt,B为y+nt

2).那么有(x+m*t)%L=(y+n*t)%L;
于是可以写成(x+m*t)-(y+n*t)=p*L;(p为任意整数)
即(m-n)*t+p*L=y-x;  

3).令a=m-n,b=y-x;
简写成a*t+p*L=b
那么求解题目就化成求解满足方程a*t+p*L=b的最小t值的解了(明显t>=0)

 4).多项式中有一条定理,存在u(x),v(x),使得f(x)u(x)+g(x)v(x)=gcd(f(x),g(x)); 
同样的,数论中也有类似的定理,就是给定a,b,存在u,v,使得au+bv=gcd(a,b);
而且,对任意的u,v,设n=au+bv,有gcd(a,b)|n。因为gcd(a,b)|a,gcd(a,b)|b,
两边同除gcd(a,b),得到n/gcd(a,b)为整数.所以gcd(a,b)|n。
从这里可以看出,方程a*t+p*L=b类似以上的形式。只是这时的b不一定是满足解的  

5).那么如何去解a*t+p*L=b呢?由以上的启发,我们可以马上得出无解的情况,
就是如果gcd(a,L)不整除于b,那么此式一定无解。那有解得情况呢?

6).如下分析:把问题转换为:对于已知a,b,n,求x,y满足ax+by=n,(gcd(a,b)|n)
(此时的a对应上面的a,b对应L,x,y分别对应t,p,n对应b,这样写是为了把问题分离出来研究)

下面解决有解的情况(以下看起来有些绕,大家不妨拿个纸笔演算下)
  假如d=gcd(a,b),下面求满足方程的一个特解。
  假设x’,y’满足ax’+by’=d(我们这里是假设,如何求出这个解后面介绍)
       那么两边同时乘以n/d;
       可得:a*n*x’/d+b*n*y’/d=n;
       比较ax+by=n;
       有x0=n*x’/d,y0=n*y’/d;(x0,y0为ax+by=n的一个特解)          

7).接下来就是求通解:
于是将ax+by=n两边同除d,再令a’=a/d,b’=b/d,n’=n/d;
明显这时有gcd(a’,b’)=1;
则有a’x+b’y=n’;
由a’x+b’y=n’与a’*x0+b’*y0=n’;
可得a’(x-x0)+b’(y-y0)=0;
因为gcd(a’,b’)=1,所以有a’|(y-y0)
则可令y=y0-a’*t(t为整数),那么可得x=x0+b’*t
所以y=y0-a’*t,   x=x0+b’*t即为ax+by=n的通解.

y = (n*y'/d)-(a/d)*t;
x = (n*x'/d)+(b/d)*t;     

8).而这时我们求的t ,就是这里的x
而minx=x%b’=(x0+b’*t)%b’=x0%b’=(n*x’/d)%b’
这时要注意:如果minx为负数,取模之后为小于0大于-b’的数,
所以此时只要minx=minx+b’,就可以让minx取最小正整数,就是我们要的解.
我们再回到之前的假设: x’,y’满足ax’+by’=d
就是把问题又化为解ax+by=d的x,y值.    

9).大家对辗转相除法一定不陌生:对于整数a,b,有gcd(a,b)=gcd(b,a%b);
从这里我们可以获取一些信息
我们将ax+by=d往下推一层: 存在x’,y' ,使得b*x’+(a%b)y’=d
又a%b=a-b*(a/b);
于是b*x’+( a-b*(a/b))y'=d;
展开可化成a*y’+b*(x’-(a/b)*y’)=d;
于是有x=y’,y=x'- (a/b)*y';
那么由此使用递归,就可以求出a,b对应的x,y值了(这时x,y的值是唯一的)
还有就是要确定边界情况,a*1+0*0=gcd(a,0)=a;
这就是扩展欧几里得。
#include<cstdio>

long long ect_gcd ( long long a, long long b, long long &x, long long &y )
{
    long long tmp, ret;
    if ( b == 0 )
    {
        x = 1, y = 0;
        return a;
    }
    ret = ect_gcd ( b, a%b, x, y );
    tmp = x; x = y; y = tmp - a / b * y;
    return ret;
}

int main()
{
    long long x, y, m, n, l;
    while ( scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l) != EOF )
    {
        long long a = n - m, b = l, c = x - y;
        long long d = ect_gcd ( a, b, x, y );
        if ( c % d != 0 )
        {
            printf("Impossible\n");
            continue;
        }
        x = ( x * c / d ) % ( b / d );
        if ( x < 0 ) x += b / d;
        printf("%lld\n",x);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值