洛谷 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;
}
原文地址:https://www.cnblogs.com/Andy-park/p/13740947.html