POJ_1061_青蛙的约会_扩展欧基里德

这辈子就指着AC题目活了。


题意:

一根长为l的环形数轴上有两只青蛙,他们的初始位置坐标分别为x和y,他们同时开始向一个方向跳,A青蛙一次跳m米,B青蛙一次跳n米,每次跳的时间相等,如果某一时刻他们处于同一个坐标,则相遇,问他们能否相遇?若能最少要跳多少次。


Input

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

Output

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


数据范围很大,第一次做这个题的时候试图跑循环,结果TLE到死,方程很好列不再写,用扩展欧几里的算法,下面详写一下欧几里的算法。

一. 欧几里的算法,辗转相除法。
用来求两个整数最大公因数的算法,是其扩展算法的基础。
int gcd(int x,int y){
	return y==0?x:gcd(y,x%y);
}
要证明辗转相除法的正确性,就要证明gcd(x,y)==gcd(y,x%y)。
设gcd(x,y)=a,x=m*a, y=n*a
可证(x%y)|a:    x%y=x-x/y*y,  x|a, y|a, 因此(x%y)|a。
因此,a是y和x%y的公因数。
假设存在b!=a,为y和x%y的最大公因数
因为b是y的因数,且已证a是y和x%y的公因数,
不妨设b=k*a (k>1)。
则 y / b = y / ( k * a ) = n / k为整数
( x % y ) / b = ( x - x / y * y ) / b = x / b - ( x / y ) * y / b = m / k - ( x / y ) * n / k, 已证n / k为整数,所以 m|k,与m,n互质矛盾,所以a就是y和x%y的最大公因数

二. 扩展欧几里的
对于不全为0的整数对a, b,必存在整数x, y,使得gcd( a, b) = a * x+b * y。
直观地来说就是可以用这个算法求二元一次方程的一个特解,并得到通解。

int ex_gcd(int a,int b,int& x,int& y){
	if(!b){
		x=1;y=0;
		return a;
	}
	int ret=ex_gcd(b,a%b,x,y);
	int tem=x;
	x=y;
	y=tem-a/b*y;
	return ret;
}
这份代码就是求a*x+b*y=gcd(a,b)的解x, y,返回a和b的最大公因数。
算法等价于
a*x1+b*y1=gcd(a,b)
b*x2+(a%b)*y2=gcd(b,a%b)
这两个等式。
证明:
由欧几里的算法可知,gcd(a,b)=gcd(b,a%b),因此a*x1+b*y1=b*x2+(a%b)*y2
右边=b*x2+( a - a / b * b )*y2=a * y2 + b * ( x2 - a / b * y2).
由于等式恒等,所以x1=y2, y1=x2-a/b*y2

得到特解x0, y0后,x0加减b/gcd(a,b) , y0加减a/gcd(a,b)得到其他解,
a*x+b*y=z (z为常数)    等价于    a/gcd(a,b)*x + b/gcd(a,b) *y =z*gcd(a,b),
此时x, y前面的系数互质,而x,y为整数。

题目代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long x,y,m,n,l;
long long ex_gcd(long long a,long long b,long long& x,long long& y){
	if(!b){
		x=1,y=0;
		return a;
	}
	int ret=ex_gcd(b,a%b,x,y);
	int tem=x;
	x=y;
	y=tem-a/b*y;
	return ret;
}
int main(){
	while(scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l)!=EOF){
		long long t,k;
		long long r=ex_gcd(n-m,l,t,k);
		if((x-y)%r)	puts("Impossible");
		else{
			long long ans=t*(x-y)/r;
			long long tem=l/r;
			ans=(ans%tem+tem)%tem;
			printf("%lld\n",ans);
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值