青蛙的约会(扩展欧几里得)
我们发现转圈和取模这两件事有很大联系,并且数据范围清一色的 \(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);
}