【ybt金牌导航8-6-1】【luogu P1516】青蛙约会 / 不定方程同余方程例题

青蛙约会

题目链接:ybt金牌导航8-6-1 / luogu P1516

题目大意

有两个东西在环上各自的位置往同一个方向跳,每次各会跳一定的距离。
然后问你要跳多少次,这两个东西才会在同一个地方。
如果不会在同一个地方,也要按题目要求输出表示。

思路

那我们考虑能不能列出方程。
s + m x = t + n x ( m o d   L ) s+mx=t+nx(mod\ L) s+mx=t+nx(mod L)
s , t s,t s,t 分别是两个东西出发的位置, m , n m,n m,n 是两个东西每次跳的格数。

化一下: ( m − n ) x = t − s ( m o d   L ) (m-n)x=t-s(mod\ L) (mn)x=ts(mod L)
这是一元线性同余方程,自然会想到把它变成二元一次不定方程,然后用扩展欧几里得来做。
( m − n ) x + L y = t − s (m-n)x+Ly=t-s (mn)x+Ly=ts

但是它要有解要有条件,就是 ( m − n ) (m-n) (mn) L L L 互质。
那如果不互质,就输出不可行,否则我们就继续做。

那你就把左边的两个变成互质: m − n gcd ⁡ ( m − n , L ) x + L gcd ⁡ ( m − n , L ) y = t − s gcd ⁡ ( m − n , L ) \dfrac{m-n}{\gcd(m-n,L)}x+\dfrac{L}{\gcd(m-n,L)}y=\dfrac{t-s}{\gcd(m-n,L)} gcd(mn,L)mnx+gcd(mn,L)Ly=gcd(mn,L)ts
(记得之后的模数也要除,因为你这里的 L L L 变成了 L gcd ⁡ ( m − n , L ) \dfrac{L}{\gcd(m-n,L)} gcd(mn,L)L,它就表示模数)

但是扩展欧几里得是 a x + b y = 1 ax+by=1 ax+by=1,它的等式右边是 1 1 1 啊。
那简单,我们就把式子的右边看成 1 1 1,算出 x x x,然后再把右边乘回去,就可以了。

然后你就把它弄出来,就可以输出了。

代码

#include<cstdio>
#define ll long long

using namespace std;

ll x, y, m, n, mo;
ll X, Y, a, b, c;

ll GCD(ll x, ll y) {
    
    //普通的求 gcd
	if (!y) return x;
	return GCD(y, x % y);
}

ll exgcd(ll a, ll &x, ll b, ll &y) {
    
    //扩展欧几里得求 ax+by=1
	if (!b) {
    
    
		x = 1;
		y = 0;
		return a;
	}
	
	ll re = exgcd(b, y, a % b, x);
	y -= a / b * x;
	return re;//顺便可以求出 gcd,可用可不用
}

int main() {
    
    
	scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &mo);
	
	//弄出一元线性同余方程,然后转成二元一次不定方程
	a = (m - n + mo) % mo;
	b = mo;
	c = (y - x + mo) % mo;
	
	int gcd = GCD(a, b);//判断是否有解
	if (c % gcd != 0) {
    
    
		printf("Impossible");
		return 0;
	}
	
	a /= gcd;//把式子化成 ax+by=c 且 a,b 互质的形式
	b /= gcd;
	c /= gcd;
	exgcd(a, X, b, Y);
	
	X = (X % mo + mo) % mo;
	printf("%lld", (X * c) % (mo / gcd));//算出答案(记得这里模数不是mo)
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/114157312