【数论】中国剩余定理

求如下方程组的解:

\[\begin{cases} n \equiv r_1 \pmod {m_1} \\ n \equiv r_2 \pmod {m_2} \\ \dots \dots \\n \equiv r_k \pmod {m_k} \end{cases} \]

我们把它拆成 \(k\) 个同样的同余方程组:

\[\begin{cases} C_1 \equiv r_1 \pmod {m_1} \\ C_1 \equiv 0 \pmod {m_2} \\ \dots \dots \\ C_1\equiv 0 \pmod {m_k} \end{cases} \begin{cases} C_2 \equiv 0 \pmod {m_1} \\ C_2 \equiv r_2 \pmod {m_2} \\ \dots \dots \\ C_2 \equiv 0 \pmod {m_k} \end{cases} \cdots \begin{cases} C_k \equiv 0 \pmod {m_1} \\ C_k \equiv 0 \pmod {m_2} \\ \dots \dots \\ C_k \equiv r_k \pmod {m_k} \end{cases} \]

\[M = \prod\limits_{i = 1}^{k}{m_i} \]

\[C_i = r_i \times \frac{M}{m_i} \times (\frac{M}{m_1})^{-1} \]

由同余的可加性,得:

\[n = \sum\limits_{i = 1}^{k}{C_i} \]

其中 \((\frac{M}{m_i})^{-1}\) 表示 \(\frac{M}{m_i}\) 表示其在模 \(m_i\) 下的 逆元。

对于正确性证明,分别考虑每一个 \(i\),因为 \(n\) 含有因子 \(\frac{M}{m_i}\),所以 \(\frac{M}{m_i} \mid C_k\),即 满足除 \(C_k \equiv r_i \pmod{m_i}\) 以外的所有同余方程。因为 \(\frac{M}{m_i}\)\((\frac{M}{m_i})^{-1}\) 互为逆元,所以:

\[\begin{aligned} \frac{M}{m_i} \times (\frac{M}{m_i})^{-1} \equiv 1 \pmod {m_i} \\ r_i \times \frac{M}{m_i} \times (\frac{M}{m_i})^{-1} \equiv r_i \pmod {m_i} \\ C_i \equiv r_i \pmod{m_i} \end{aligned} \]

我们构造的每个 \(C_i\) 也就可以使第 \(i\) 个方程组成立。

#include <bits/stdc++.h>
#define int __int128
using namespace std;
const int N = 15;
int n;
int m[N], r[N];
inline void read(int& x){
    int s = 0, w = 1;
    char ch = getchar();
    while(!isdigit(ch))
        w = -1, getchar();
    while(isdigit(ch))
        s = s * 10 + ch - '0', ch = getchar();
    x = s * w;
    return;
}
inline void write(int x){
    if(x < 0)
        putchar('-'), x = -x;
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
inline int exgcd(int a, int b, int& x, int& y){
    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 inv(int a, int p){
    int res, x, y;
    exgcd(a, p, x, y);
    return (x % p + p) % p;
}
inline int crt(int n, int m[], int r[]){
    int M = 1, s = 0;
    for(int i = 1; i <= n; i++) 
        M *= m[i];
    for(int i = 1; i <= n; i++)
        s = (s + r[i] * (M / m[i]) * inv(M / m[i], m[i]) % M) % M;
    s = (s + M) % M;
    return s;
}
signed main(){
    read(n);
    for(int i = 1; i <= n; i++)
        read(m[i]), read(r[i]);
    write(crt(n, m, r));
    return 0;
}
posted @ 2025-07-06 10:47  Prülystic  阅读(13)  评论(0)    收藏  举报