poj1061 poj2115 扩展欧几里德算法

以下内容为个人理解,有错误地方还请指出。

1、gcd和扩展gcd算法都要求a,b大于0;

2、ax+by=c有解的充要条件是c%gcd(a,b)==0;

3、求解ax+by=c 转换为求解a*_x+b*_y=gcd(a,b); 

   x=_x*c/gcd(a,b); y=_y*c/gcd(a,b); 

4、如果ax+by=gcd(a,b)中b<0,则取|b|带入算法计算,

   但是算出的y要加负号才是正确结果;a<0同理;

   当然也可以方程两端都乘以-1;

#define LL long long
LL exgcd(LL a,LL b,LL &x,LL &y){
	if(!b)
	{
		x=1;
		y=0;
		return a;
	}
	LL ans=exgcd(b,a%b,x,y);
	LL temp=x;
	x=y;
	y=temp-a/b*y;
	return ans;
}
LL exgcd2(LL m,LL n,LL &x,LL &y)//非递归
{
	LL x1=0,y1=1,x0=1,y0=0;
	LL r=(m%n+n)%n;
	LL q=(m-r)/n;
	x=0;y=1;
	while(r)
	{
		x=x0-q*x1; y=y0-q*y1;
		x0=x1; y0=y1;
		x1=x; y1=y;
		m=n; n=r; r=m%n;
		q=(m-r)/n;

	}
	return n;
}
//poj1061
青蛙A、B分别在周长为L的圆环的x、y处,速率分别为m,n;往同一方向前进,最少多长时间A、B相遇;
//x+m*_x=y+n*_x+L*y ==> (m-n)*_x-L*_y=y-x ;
int main()//poj1061
{
	LL x,y,m,n,L;
	scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&L);
	LL _x,_y;
	LL Gcd=exgcd(m-n>0?m-n:n-m,L>0?L:-L,_x,_y);
//如果m-n小于0,求出的_x要取其相反数,或者c取相反数,再计算
	LL c=y-x;
	if( c%Gcd || m==n)
	{
		printf("Impossible\n");
		return 0;
	}
	LL num=L/Gcd;//num为_x的最大值,一般为b/gcd(a,b)
	_x=m-n>0?_x:-_x;//或者c=m-n>0?c:-c;
	_x=_x*c/Gcd;
	_x=(_x%num+num)%num;
	printf("%lld\n",_x);
}







//poj2115
周长为1<<k的环,起点为A,终点为B,每次跳跃C,最少跳多少次到达B
A+C*x=B+(1>>k)*y ==> C*x-(1<<k)*y=B-A ;
int main()//poj2115
{
	LL A,B,C,k;
	while (scanf("%lld%lld%lld%lld", &A, &B, &C, &k), A || B || C || k)
	{
		LL _x, _y;
		LL Gcd = exgcd(C, (LL)1<<k, _x, _y);
		if ((B - A) % Gcd )
		{
			printf("FOREVER\n");
			continue;
		}
		LL num = ((LL)1<<k) / Gcd; //最大值
//最多不超过 [(1<<k)*C/(gcd(1<<k,C)] / C,
//即跳(1<<k和C的最小公倍数除以C)次后一定会回到原点;
		_x = _x * (B-A) / Gcd;
		_x = (_x % num + num) % num;
		printf("%lld\n", _x);
	}
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值