青蛙的约会(扩展欧几里得)

我们发现转圈和取模这两件事有很大联系,并且数据范围清一色的 \(10^9\),看起来就可以用同余扩欧之类的数论算法做。

考虑“跳了几次以后才会碰面”用数学语言怎样描述:设跳的次数为 \(t\),于是有 \(s_1+tm\equiv s_2+tn (\mod L)\)。根据取模的定义,我们引入参数 \(k\),将式子化为 \((s_1-s_2)+(m-n)t=kL\),移项得 \((n-m)t+Lk=(x-y)\)

这不是 \(ax+by=c\) 形的不定方程吗?直接上扩欧:

\(\left\{\begin{aligned}&a=n-m\\&b=L\\&c=sx-sy\\&x=t\\&y=k\end{aligned}\right.\)

注意几个坑点:(如果【同余方程】是模板扩欧,那这题就是模板扩欧踩坑)

  • 扩欧求的其实是 \(ax+by=\gcd(a,b)\) 的解,答案必须乘以 \(\frac{c}{\gcd(a,b)}\)
  • 因为是跳的次数,所以要求最小正整数解,b/=g,print((x%b+b)%b)
  • 如果 \(a\) 是负数,必须将 \(a,c\) 一起取反而不用取反 \(b\)——\(b\) 取不取反对不定方程的答案没有影响,而 \(\gcd(a,b)\) 显然要求两者皆为非负,所以你既不需要、也不应该取反 \(b\)
  • 不开 long long 见祖宗。

下面是 AC 代码(略去扩欧函数):

lint sx, sy, m, n, L;
scanf("%lld%lld%lld%lld%lld", &sx, &sy, &m, &n, &L);
lint a = n - m, b = L, x, y, c = sx - sy;
if (a < 0) c = -c, a = -a;
lint g = exgcd(a, b, x, y);
if (c % g) puts("Impossible");
else {
	x = x * c / g , b = b / g;
	printf("%lld\n", (x % b + b) % b);
}

THE END

posted @ 2021-11-17 19:36  q0000000  阅读(29)  评论(0编辑  收藏  举报