中国剩余定理(CRT)
💡 核心思想
中国剩余定理(CRT)求解一组同余方程:x ≡ a₁ (mod m₁), x ≡ a₂ (mod m₂), ..., x ≡ aₙ (mod mₙ)。当模数两两互质时,有唯一解 x ≡ Σ aᵢ · Mᵢ · inv(Mᵢ, mᵢ) (mod M),其中 M = ∏ mᵢ,Mᵢ = M / mᵢ。当模数不互质时,需要逐对合并方程,使用扩展欧几里得判断是否有解。
CRT 是数论中的经典定理,竞赛中常用于处理多模数问题。两两互质的情况直接用公式,不互质的情况逐对合并。很多选手只背了互质版本的公式,但竞赛中经常遇到不互质的情况,必须掌握 exgcd 合并法。
🎯 直觉理解
想象你在安排一个周期性的活动。你每 3 天去一次健身房,每 5 天去一次图书馆,每 7 天去一次电影院。问多少天后你第一次三项活动同一天进行?这就是 CRT:找一个数同时满足被 3 除余某个数、被 5 除余某个数、被 7 除余某个数。
📝 算法流程
- 两两互质版本:
- 计算 M = m₁·m₂·...·mₙ
- 对每个 i,计算 Mᵢ = M / mᵢ
- 计算 invᵢ = Mᵢ 在模 mᵢ 下的逆元(exgcd)
- 答案 x = Σ aᵢ·Mᵢ·invᵢ (mod M)
- 不互质版本:逐对合并两个方程,用 exgcd 求解
$$x \equiv \sum_{i=1}^{n} a_i \cdot M_i \cdot M_i^{-1} \pmod{M},\quad M = \prod m_i,\ M_i = M / m_i$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | $O(n \log M)$ |
| 空间 | $O(n)$ |
💻 参考实现(C++)
C++ (C++17)
#include
using namespace std;
using ll = long long;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) { x = 1; y = 0; return a; }
ll x1, y1, g = exgcd(b, a % b, x1, y1);
x = y1; y = x1 - (a / b) * y1;
return g;
}
ll inv(ll a, ll mod) {
ll x, y;
ll g = exgcd(a, mod, x, y);
return (x % mod + mod) % mod;
}
// 中国剩余定理(模数两两互质)
ll crt(vector& a, vector& m) {
int n = a.size();
ll M = 1;
for (ll mi : m) M *= mi;
ll res = 0;
for (int i = 0; i < n; i++) {
ll Mi = M / m[i];
res = (res + a[i] * Mi % M * inv(Mi, m[i]) % M) % M;
}
return res;
}
// 合并两个同余方程:x ≡ a1 (mod m1), x ≡ a2 (mod m2)
pair merge(ll a1, ll m1, ll a2, ll m2) {
ll x, y;
ll g = exgcd(m1, m2, x, y);
if ((a2 - a1) % g != 0) return {-1, -1}; // 无解
ll lcm = m1 / g * m2;
ll t = ((a2 - a1) / g * x % (m2 / g) + (m2 / g)) % (m2 / g);
ll a = (a1 + m1 * t) % lcm;
return {a, lcm};
}
int main() {
vector a = {2, 3, 2};
vector m = {3, 5, 7};
cout << crt(a, m) << endl; // 23
return 0;
} ⚠️ 常见坑点
模数不互质——互质版本公式失效,需要用 exgcd 合并
逆元不存在——Mᵢ 和 mᵢ 必须互质(由 CRT 条件保证)
结果溢出——中间乘法要用 __int128 或取模
合并顺序——不互质版本要逐对合并,不能一次处理所有
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P1495 曹冲养猪 | 洛谷 | CSP-S | CRT 模板题 |
| P3868 中国剩余定理 | 洛谷 | CSP-S | CRT + 大数 |
| P4774 龙 | 洛谷 | CSP-S | CRT 扩展 + 循环 |