wustoj&&poj青蛙的约会(扩展欧几里得,最小整数解)

前段时间整理了一下关于乘法逆元的集中求法,不过一直有的问题没有注意,今天做了一个题目最后求出来的解需要化为最小正整数,这个知识点以前没有涉及到,今天看到了就作了了解,现在在这里补充一下,就举今天看到的比较经典的例子吧:

题目如下:

青蛙的约会

Description

        两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。

       我们把这两只青蛙分别叫做青蛙 A 和青蛙 B ,并且规定纬度线上东经 0 度处为原点,由东往西为正方向,单位长度 1 米,这样我们就得到了一条首尾相接的数轴。设青蛙 A 的出发点坐标是 x ,青蛙 B 的出发点坐标是 y 。青蛙 A 一次能跳 m 米,青蛙 B 一次能跳 n 米,两只青蛙跳一次所花费的时间相同。纬度线总长 L 米。现在要你求出它们跳了几次以后才会碰面。

Input

输入只包括一行 5 个整数 x,y,m,n,L。

0≤x,y<2×10​9​​  , 0<m,n<2×109​​  , 0<L<2.1×109 。 保证 x≠y

Output

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

Sample Input 

1 2 3 4 5

Sample Output

4

 我们对于这个问题稍加分析,我们就可以列出式子:

(m-n)t-pl=y-x;

这个式子我们不是很熟悉,我们将左右两边同时加上负号过后,这个式子可以化为(n-m)t + pl = x-y;

这个式子就是我们非常熟悉的ax+by=c 这种形式的式子了,可以看出是扩展欧几里得的求解,

当然我们最开始的时候我们可以有一组特解是ax0+by0=gcd(a,b),这里比较特殊一点的就是a,b互为质数,这个地方就和我们上一篇整理的主题求逆元其实是十分相似的,

就如我上一篇帖子说的:

这个和逆元有什么关系呢,举个例子:求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解

这个就是来求相应的逆元,题目一般会保证b为素数,然后我们就可以得出gcd(a,b)=1,所以原式可以转化为ax-kb=1;

我们设y=-k;那么原式就变成了ax+by=1;那么也就是扩展欧几里得算法,我们递归x的值求出来就好了

这里稍稍的略提一下。

好了,然后我们可以令ta=(n-m),tb=l,tc=x-y;

然后带入求解(temp)即可

我们知道最开始的时候式子的原型为:ax0+by0=gcd(a,b)                      ①

然后两边乘以c/gcd(a,b),然后式子就变成了a*(x0*c/gcd(a,b))+b*(y0*c/gcd(a,b))=c;                   ②

所以这个时候式子的两组解就变成了 x1=x0*c/gcd(a,b),x2=y0*c/gcd(a,b)

我们要求的是最小解,然后就是我们应该减去一点什么,然后减到最小就可以了,当x1减去1的时候,我们就要减去c/gcd(a,b)

所以,当我们减去x的时候,我们就要减去x*a*c/gcd(a,b),为了保持式子的平衡,y1这个时候需要加上y*b*c/gcd(a,b)

所以,最终这个式子到达平衡:x*a*c/gcd(a,b)==y*b*c/gcd(a,b),我们将这个式子化简我们就可以得到

:x/y=(b/gcd(a,b)/(a/gcd(a,b)); 为什么这个地方没有把gcd(a,b)同时约掉呢,因为最后出来要对结果进行取余,当x越小的时候,最后得出来的整数解越小

所以,在我们将ta,tb,x,y,代入了以后,我们求出来的就相当于我们①中的形式,所以我们最后求出来的小姑要达到②的样子,我们第一步就是首先将最开始带入的得到的特解乘以tc/gcd(a,b),

第二步,因为每次减去一个一就像我最开始说过的那个样子减去这个式子里面x/y=(b/gcd(a,b)/(a/gcd(a,b));对应的分子和分母,

一直减到k,再减一个(k+1)就小于零的情况,可以理解为x2=x1-kt,这个式子可以化为x2=x1%kt;

而这个地方的kt对于x而言就是b/gcd(a,b),所以第二步就是写出中间变量为mod=b/gcd(a,b);最后的结果对于它取余就可

还有最后一个关心的地方,就是扩展欧几里得最后求出来的结果可能是负数,所以这个时候我么就需要进行一定的处理

当它大于0的时候我们直接mod,当它小于0的时候,我们mod完了之后再加上一个mod就可以了,当然这些步骤都是在

tc%temp==0的前提下,最后我们看一下这个题目的数据知道需要注意数据范围,于是用long long ,这个地方存在有可能ta<0的情况,注意,当ta<0的时候,ta变成正的时候tc也要变成正,因为正数相加不会得到负数,这里注意一下就行了,最后上代码:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,x,y);
	ll tx=x,ty=y;
	x=ty;
	y=tx-(a/b)*ty;
	return gcd;
}
int main()
{
	ll x,y,m,n,l,flag,ans;
	while(~scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l))
	{
		flag=0;
		ll ta=n-m,tb=l,tc=x-y;
		if(ta<0)
		{
			ta=-ta;
	    	tc=-tc;
		}
		ll temp=exgcd(ta,tb,x,y);
		if(tc%temp!=0)
		flag=-1;
		else
		{
			x=x*(tc/temp);
			ll mod=tb/temp;
			if(x>=0)
			ans=x%mod;
			else
			ans=(x%mod+mod)%mod;
		}
		if(flag==0)
		printf("%lld\n",ans);
		else if(flag==-1)
		printf("Impossible\n"); 
	}
	return 0;
}

本题解答就到此结束啦~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值