题目传送门

1、将式子等价转换

考虑前两个式子:

\(x≡m_1(\%\ a_1)\)

\(x≡m_2(\%\ a_2)\)

变形:

\(x=k_1∗a_1+m_1\)

\(x=k_2∗a_2+m_2\)

联立消掉\(x\),得到:

\(k_1∗a_1+m_1=k_2∗a_2+m_2\)

移项:

\(k_1∗a_1−k_2∗a_2=m_2−m_1\)

也就是:

\(① \ \ \ \ k_1∗a_1+k_2∗(−a_2)=m_2−m_1\)

本题预求:一个最小的非负整数\(x\)

因为要求\(x\)最小,而\(a_i\)\(m_i\)都是正数,也就是需要找到一个最小的\(k_1,k_2\),使得等式成立。

那么如何找到\(k_1,k_2\)的最小解呢?方程\(①\)其实就是形如\(ax+by=c\)的一个不定方程,可以考虑使用扩展欧几里得来求最小解。

扩展欧几里得来求最小解的步骤:

  1. 利用扩展欧几里得计算出方程\(ax+by=gcd(a,b)\)的一组解\(x_0,y_0\)

  2. 通过变换公式得到\(ax+by=c\)的特解\(x_1=x_0∗c/d, y_1=y_0∗c/d;\)

  3. 最小解:\(int s=b/d; x_{min}=(x_1\%s+s)\%s\);

2. 用扩展欧几里得算法找出一组解

已知\(a_1,m_1,a_2,m_2\),可以用扩展欧几里得算法算出一个\(k′_1,k′_2\)使得:

\(② \ \ \ \ k′_1∗a_1+k′_2∗(−a_2)=gcd(a_1,−a_2)\) 【这是在计算\(ax+by=gcd(a,b)\)的一组解】


无解判断
\(gcd(a_1,−a_2)∤(m_2−m_1)\),则无解,返回即可。


如果有解
\(d=gcd(a_1,−a_2),y=\frac{m_2−m_1}{d}\)

连立\(①\)式和\(②\)式:
我们只需要将\(k′_1\)\(k′_2\)分别扩大\(y\)倍,则可以以找出一个\(k_1,k_2\)满足\(①\)式:
\(k_1=k′_1∗y,k_2=k′_2∗y\) 【这是在求\(ax+by=c\)的特解】

3. 找到最小正整数解

有了\(ax+by=c\)的特解,就可以来求方程的通解和最小解了。

我们知道一个性质:

\(③ \ \ \ \ k_1=k_1+k∗\frac{a_2}{d}\)
\(\ \ \ \ \ \ \ k_2=k_2+k∗\frac{a_1}{d}\)

\(k\)为任意整数,这时新的\(k_1,k_2\)仍满足\(①\)式。


性质证明:
将新的\(k_1,k_2\)带入式子\(k_1∗a_1−k_2∗a_2=m_2−m_1\)得:

\((k_1+k∗\frac{a_2}{d})∗a_1+(k_2+k∗\frac{a_1}{d})∗(−a_2)=m_2−m_1\)

拆出来:

\(k_1∗a_1+k∗\frac{a_2∗a_1}{d}+k_2∗(−a_2)+k∗\frac{a_1∗(−a_2)}{d}=m_2−m_1\)

交换一下顺序,把负号拆出来:

\(k_1∗a_1+k_2∗(−a_2)+k∗\frac{a_2∗a_1}{d}−k∗\frac{a_1∗a_2}{d}=m_2−m_1\)

那个同加同减可以消掉:

\(k_1∗a_1+k_2∗(−a_2)=m_2−m_1\)

这个式子和\(①\)是一样的,因\(①\)成立,故此式也成立。


有了上面的性质,要找一个最小的非负整数解,我们只需要让

\(k_1=k_1\ \ \%\ \  abs(\frac{a_2}{d})\)

\(k_2=k_2\ \ \%\ \  abs(\frac{a_1}{d})\)

就是在\(k_1\)中尽可能的去掉\(abs (\frac{a_2}{d})\),就可找到当前最小的\(k_1,k_2\)的解,也就是不断的减小\(k\)的值,直至\(k\)\(0\),简单点来说是,就是一步模完。

\(Q\):此处为什么要取绝对值呢?

\(A\):因为不知道\(\frac{a_2}{d}\)的正负性,我们在原基础上要尽量减多个\(abs(\frac{a_2}{d})\),使其为正整数且最小。

4. 等效替代

\(②\)式带入\(x=k_1∗a_1+m_1\)

则:

\(x=(k_1+k∗\frac{a_2}{d})∗a_1+m_1\)

\(=k_1∗a_1+m_1+k∗\frac{a_2∗a_1}{d}\)

\(=k_1∗a_1+m_1+k∗lcm(a_1,a_2)\)

\(Q\):这里,\(k\)都为\(0\)了,为什么还要算呢?

\(A\):因为这只是前两个式子得最小\(k\),有可能遇到下一个式子后面被迫要扩大

\(③\)中,我们设\(a_0=lcm(a_1,a_2)\),\(m_0=k_1∗a_1+m_1\) 【之所以这样设,是因为\(a_1,a_2,k_1,m_1\)都是已知数,可以算出来,未知的是\(x,k\)
那么:

\(③ =k∗a_0+m_0\)

这个形式与一开始我们分解的形式是不是特别像呢?

没错!假设之后又来了一个\(a3,m3\)

我们只需要继续找:

\(x=k∗a_0+m_0=k_3∗(−a_3)+m_3\),那么问题又回到了第一步。

5.总结

我们的做法相当于每次考虑合并两个式子,将这\(n\)个式子合并\(n−1\)次后变为一个式子。最后剩下的式子就满足我们的答案。

注意:

  1. \(lcm(a_1,a_2)\)\(\%\frac{a_2}{d}\),需要取绝对值。又因为\(d=gcd(a_1,−a_2)\),我们不知道\(a_1\)的正负性(可能是上一步推过来的)。

  2. \(\%\frac{a_2}{d}\),需要取绝对值, 模负数的话,不会取到正解;

6、完整代码

#include <iostream>

using namespace std;
typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main() {
    int n;
    cin >> n;

    //先读入第一个方程
    LL a1, m1;
    cin >> a1 >> m1;

    //依次读入n-1个方程,每次合并一个方程
    for (int i = 0; i < n - 1; i++) {
        LL a2, m2;
        cin >> a2 >> m2;
        //开始合并
        LL k1, k2;
        //a1*k1+(-a2)*k2=m2-m1
        LL d = exgcd(a1, -a2, k1, k2);
        if ((m2 - m1) % d) { //如果不是0,则无解
            cout << -1;
            exit(0);
        }
        //求 ax+by=c的特解
        k1 *= (m2 - m1) / d;

        //使得k1成为方程的最小正整数解
        int t = abs(a2 / d);
        k1 = (k1 % t + t) % t;

        //需要更新a1和m1了
        m1 = k1 * a1 + m1;
        a1 = abs(a1 / d * a2);
    }
    cout << (m1 % a1 + a1) % a1;
    return 0;
}