洛谷 P1516 青蛙的约会
Solution
先列出最简单的柿子:
\[(X+mT) \% L=(Y+nT) \% L
\]
下面推导过程:
\[(X + mT) \% L = (Y + nT) \% L
\]
\[⇒ X + mT \equiv Y + nT (\%L)
\]
\[⇒ (m-n)T \equiv Y-X (\% L)
\]
其中 \(m-n\), \(Y-X\) 已知,分别设为 \(a\), \(c\), 上面的柿子可以写成如下形式:
\[aT + yL = c
\]
如果用 \(x\) 表示 \(T\), \(b\) 表示 \(L\),这个柿子还可以变成更优美的形式:
\[ax + by = c
\]
\[(a=m-n, b=L, c=Y-X)
\]
这就变成线性同余方程的一般柿子了。
考虑用 exgcd 求出一组对于 \(ax+by=gcd(a,b)\) 的特解,记为 \(x_0\), \(y_0\)。显然:
\[ax_0+by_0=gcd(a, b)
\]
\[⇒ ax_0 \frac{c}{gcd(a,b)} +by_0 \frac{c}{gcd(a,b)} = gcd(a,b) \frac{c}{gcd(a,b)}
\]
\[⇒ a \frac{x_0c}{gcd(a,b)} + b \frac{y_0c}{gcd(a,b)} = c
\]
于是我们对原方程求出了一组特解:
\[x = \frac{x_0c}{gcd(a,b)}, y = \frac{y_0c}{gcd(a,b)}
\]
考虑通解。设 \(x, y\) 是已求出的特解,则有:
\[ax+by=a(x+bd)+b(y-ad)
\]
要使 \(bd, ac\) 最小且为整数,\(d\) 的最小值为 \(\frac{1}{gcd(a,b)}\)。因此:
\[x_{min}=x \% \frac{b}{gcd(a,b)}
\]
注意负数取模。
另外,如果计算前 \(a\) 是负数,就对 \(a\) 和 \(c\) 取相反数。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
long long X, Y, m, n, l, a, b, c, x, y, g;
long long gcd(long long x, long long y) { return y == 0 ? x : gcd(y, x % y); }
void exgcd(long long a, long long b)
{
if(b == 0)
{
x = 1, y = 0;
return ;
}
exgcd(b, a % b);
long long tx = x;
x = y, y = tx - (a / b) * y;
}
int main()
{
scanf("%lld%lld%lld%lld%lld", &X, &Y, &m, &n, &l);
a = m - n, b = l, c = Y - X, g = gcd(a, b);
if(a < 0) a = -a, c = -c;
g = gcd(a, b);
if(c % g != 0) { printf("Impossible"); return 0; }
exgcd(a, b);
printf("%lld", ((x * (c / g)) % (b / g) + (b / g)) % (b / g));
return 0;
}