POJ 1061 (扩展欧几里得)

博客分析了POJ 1061题目的解决方案,探讨了两只青蛙在周长为L的圆环上,按不同步长m和n跳跃,判断是否能相遇的问题。通过扩展欧几里得算法,得出t(m-n)-kL=y-x,进而讨论了方程解的存在条件和求解方法,包括负数取模和求最小正解的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

有两只青蛙在圆环上,圆的周长为LLL,出生点分别为xxx,yyy。第一只青蛙每次可以跳mmm距离远,第二只青蛙每次可以跳nnn距离远,现在问你如果两只青蛙同时一直往一个方向跳,是否能过碰面。能的话输出跳的次数,不能碰面就输出“Impossible”

分析

  首先我们列几个式子分析一下,假设他们跳了ttt次且都往右边跳,那么对于第一只青蛙AAA有如下等式 la=mt+xl_a=mt+xla=mt+x,对于第二只青蛙 BBBlb=nt+yl_b=nt+ylb=nt+y。最后他们要碰面,而他们在圆环上,则肯定满足 la=lb+kLl_a = l_b+kLla=lb+kL。将上面的式子代入我们可以得到mt+x=nt+y+kLmt+x=nt+y+kLmt+x=nt+y+kL。化简可以得到t(m−n)−kL=y−xt(m-n)-kL=y-xt(mn)kL=yx。其中m−n,L,y−xm-n,L,y-xmn,L,yx都是已知的。那么我们很容易联想到扩展欧几里得ax+by=gcd(a,b)ax+by=gcd(a,b)ax+by=gcd(a,b),可以解出x,yx,yx,y的一个特解。

  这里我们先暂停一下,紫书上面对于扩展欧几里得有两个结论

  • a,b,ca,b,ca,b,c为任意整数,若方程ax+by=cax+by=cax+by=c的一组整数解为(x0,y0)(x_0,y_0)(x0,y0),则它的任意整数解都可以写成(x0+kb′,y0+ka′)(x_0+kb',y_0+ka')(x0+kb,y0+ka),其中a′=a/gcd(a,b),b′=b/gcd(a,b)a'=a/gcd(a,b),b'=b/gcd(a,b)a=a/gcd(a,b),b=b/gcd(a,b)

  • a,b,ca,b,ca,b,c 为任意整数,g=gcd(a,b)g = gcd(a,b)g=gcd(a,b),方程ax+by=gax+by=gax+by=g的一组解是(x0,y0)(x_0,y_0)(x0,y0),则当cccggg的倍数时ax+by=cax+by=cax+by=c的一组解是(x0cg,y0cg)(\frac {x_0c}{g},\frac {y_0c}{g})(gx0c,gy0c)。当ccc不是ggg的倍数时无整数解

我们要求的是ttt的最小值,由第二条性质我们可以知道,当(y−x)(y-x)(yx)gcd(m−n,L)gcd(m-n,L)gcd(mn,L)的倍数时,才有解,否则他们不可能相遇。由第一条性质我们可以不断的减去L/gcd(L,m−n)L/gcd(L,m-n)L/gcd(L,mn)得到ttt的最小值

但是我看有的题解是直接用的b好像也可以,感觉是不是数据很弱 有哪里我没弄明白的?然后还有个问题就是负数。我看网上好多代码都是只保证了是ax+by=cax+by=cax+by=c前面的aaa是正数,在本题中就是保证m−nm-nmn是正数,这种情况下ccc就是y−xy-xyx,否则就是n−mn-mnmx−yx-yxy。但是如果这样的话上面方程的ccc是负数怎么办,看起来好像不影响结果。

还有一点就是得到了t的一个通解以后,如何求恰好大于0的那个解。如果ttt是正数,那么直接模掉就行了,如果是负数那么就要先求模,然后加上模在求模。这个就涉及到了负数求模。在c++/java中余数 = 被除数 - (被除数/除数)* 除数。也就是说负数取模可以理解成不带负号取模,然后再把负号加上去??看起来好像是这样吧。。。

还有就是紫书上的两个性质,这个可以自己下去推一推,也比较好推。不想推的记住好像也可以? 数论的题做起来真难受。

代码

#include <iostream>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
	if (a == 0 && b == 0) return -1;
	if (b == 0) { x = 1; y = 0; return a; }
	ll d = exgcd(b, a%b, y, x);
	y -= a / b * x;
	return d;
}
int main()
{
	ll x, y, m, n, L;
	while (cin >> x >> y >> m >> n >> L)
	{
		ll a = m - n;
		ll b = L;
		ll c = y - x;
		ll xx, yy;
		if (a < 0)
		{
			a = -a;
			c = -c;
		}
		ll gcd = exgcd(a, b, xx, yy);
		if (c%gcd) cout << "Impossible" << endl;
		else
		{
			xx = xx * c / gcd;
			ll bb = b/gcd;
			xx = (xx%bb + bb) % bb;
			cout << xx << endl;
		}
		
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值