【数论】扩展中国剩余定理

求下列同余方程组的解。

\[\begin{cases} x \equiv r_1 \pmod {m_1} \\ x \equiv r_2 \pmod {m_2} \\ \cdots \cdots \\ x \equiv r_n \pmod {m_n} \end{cases} \]

考虑先从一对同余方程的共同解开始思考。

\[\begin{cases} x \equiv r_1 \pmod {m_1} \\ x \equiv r_2 \pmod {m_2} \end{cases} \]

则有 \(x = pm_1 + r_1 = qm_2 + r_2\)。移项,得 \(pm_1 - qm_2 = r_2 - r_1\)

\(d = \gcd(m_1, m_2)\),注意到此不定方程:

\(d \mid (r_2 - r_1)\),通解如下:

\[p = p_0 + \frac{m_2}{d}\times k, q = q_0 - \frac{m_1}{d} \times k \]

\(d \nmid (r_2 - r_1)\) ,此不定方程无解,这对同余方程也无解。

\(p\) 带入原式,有

\[\begin{aligned} x &\; = (p_0 + \frac{m_2}{d} \times k)\times m_1 + r_1 \\ & \; = m_1p_0 + \frac{m_1m_2}{d} \times k + r_1 \\ & \; = k\times \operatorname{lcm}(m_1, m_2) + (m_1p_0 + r_1) \end{aligned} \]

那么:

\[x \equiv m_1p_0 + r_1 \pmod{\operatorname{lcm}(m_1, m_2)} \]

因为 \(x = pm_1 + r_1\),我们希望求最小正整数 \(x\)\(p\) 就必须在大于零的情况下尽可能小。所以:

\[p' = (p_0 \operatorname{mod} \frac{m_2}{d} + \frac{m_2}{d}) \operatorname{mod} \frac{m_2}{d} \]

所以在 \(p\) 取最小值时,这两个方程可以合并成:

\[x \equiv (m_1p' + r_1) \pmod {\operatorname{lcm}(m_1, m_2)} \]

将这种操作做 \(n - 1\) 次,不断合并,求出最后一次合并后的同余方程的解,即同时满足原 \(n\) 个同余式。这种方法避开了基础中国剩余定理需要求逆元的操作,所以不需要模数 \(m_1\dots m_n\) 互质,适应性更强,所以叫做扩展中国剩余定理 excrt

代码中需要注意对于正负数的处理。

#include <bits/stdc++.h>
#define int __int128_t // 注意 __int128_t 没有标准输入输出,需要自己写输入输出方式
using namespace std;
const int N = 1e5 + 5;
int n, m[N], r[N];
inline int exgcd(int a, int b, int& x, int& y){ // exgcd 解出不定方程的特解
    if(b == 0){
        x = 1, y = 0;
        return a;
    }
    int res = exgcd(b, a % b, x, y), t = x;
    x = y, y = t - a / b * y;
    return res;
}
inline int f(int a, int p){ // 将一个数在模 p 意义下补正
    return (a % p + p) % p;
}
int excrt(){
    int m1, m2, r1, r2, p0, y, c, d, lcm;
    m1 = m[1], r1 = r[1];
    for(int i = 2; i <= n; i++){
        m2 = m[i], r2 = r[i];
        d = exgcd(m1, m2, p0, y), c = r2 - r1;
        if(c % d) // 判断无解
            return -1;
        p0 = f(p0 * c / d, m2 / d), lcm = m1 * m2 / d; // 求出 p'
        r1 = f(m1 * p0 + r1, lcm), m1 = lcm; // 更新新的同余方程的 m1, r1
    }
    return f(r1 % m1, m1); // 求最终的方程的解
}
inline void read(int& x){
    int s = 0, w = 1;
    char ch = getchar();
    while(!isdigit(ch)){
        w = (ch == '-' ? -1 : 1);
        ch = getchar();
    }
    while(isdigit(ch)){
        s = s * 10 + ch - '0';
        ch = getchar();
    }
    x = s * w;
    return;
}
inline void write(int x){
    if(x < 0)
        x = -x, putchar('-');
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
    return;
}
signed main(){
    read(n);
    for(int i = 1; i <= n; i++)
        read(m[i]), read(r[i]);
    write(excrt());
    return 0;
}
posted @ 2025-07-06 09:15  Prülystic  阅读(13)  评论(0)    收藏  举报