【数论】扩展中国剩余定理
求下列同余方程组的解。
\[\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;
}

浙公网安备 33010602011771号