中国剩余定理
中国剩余定理(CRT)
问题模型
求解同余方程组,其中模数 \(m_1,m_2,\dots,m_k\) 两两互质:
构造思想
同余方程可以有任意多个,不影响求解。构造一个整数 \(x\),由 \(k\) 部分求和得到,每一部分只负责满足一个方程,而不影响其他方程。
令 $M=m_1\ast m_2\ast \cdots\ast m_k $
对于第 \(i\) 个方程,需要构造一个 \(T_i\),满足:
- \(T_i \equiv 1(mod\; m_i)\)
- \(T_i \equiv 0(mod\; m_j),j\neq i\)
构造步骤:
-
令 \(M_i=M/m_i\),此时可得 \(M_i\equiv 0(mod\; m_j),j\neq i\)
-
求 \(t_i = M_i^{-1}(mod\; m_i)\)
-
\(T_i=M_it_i\)
通解公式
#include <iostream>
using namespace std;
using LL = long long;
const int N = 17;
LL a[N], b[N];
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL gcd = exgcd(b, a%b, y, x);
y -= a / b * x;
return gcd;
}
int main() {
int n;
scanf("%d", &n);
LL x, y, tot = 1;
for (int i = 1; i <= n; ++i) {
scanf("%lld%lld", a + i, b + i);
tot *= a[i];
}
LL ans = 0;
for (int i = 1; i <= n; ++i) {
LL Mi = tot / a[i];
exgcd(Mi, a[i], x, y);
x = (x%a[i] + a[i]) % a[i];
(ans += (__int128)Mi * b[i] % tot * x % tot) %= tot;
}
printf("%lld\n", ans);
}
扩展中国剩余定理(EXCRT)
当 \(m_1,m_2,\dots,m_k\) 不满足两两互质的条件时,需要使用此方法。
对同余方程逐个合并
考虑前两个方程:
等价于 \(x=k_1m_1+a_1=k_2m_2+a_2\)
整理可得 \(m_1k_1-m_2k_2=a_1-a_2\)
将其看成关于 \(k_1,k_2\) 的不定方程 \(Ax+By=C\),其中 \(A=m_1,B=m_2,C=a_2-a_1\)
-
先使用 exgcd 判断是否有解:令 \(d=gcd(m_1,m_2), L = lcm(m_1,m_2),\)若 \(C\not\equiv 0(mod\; d)\),则该方程组无解;否则继续第 2 步
-
求特解:通过第一步算出的系数 \(k_1',k_2'\) 可得 \(k_1'm_1+k_2'm_2=d\),两边同时乘以 \(C/d\),可得 \(m_1\cdot(C/d\cdot k_1')+m_2(C/d\cdot k_2)=C\),即 \(k_1 = C/d\cdot k_1'\) 是一个特解
-
对方程左边进行调整(\(\pm L\cdot t\))可以得到无穷多组解(\(t\in Z\)):
前两个方程的通解形式:\(K=C/d\cdot k_1'+L/m_1\cdot t\)
- 回代:将 \(K\) 代回 \(x=k_1m_1+a_1\),得
即得 \(x\equiv (C/d\cdot k_1'\cdot m_1 + a_1)(mod\; L)\)
令 \(a_1=C/d\cdot k_1'\cdot m_1 + a_1,b_1=L\),继续迭代后面的同余方程即可。
#include <iostream>
using namespace std;
using LL = long long;
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL gcd = exgcd(b, a%b, y, x);
y -= a / b * x;
return gcd;
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
LL n, a, b, aa, bb, c, gcd, x, y, lcm;
cin >> n >> a >> b;
while (--n) {
cin >> aa >> bb;
gcd = exgcd(a, aa, x, y);
c = bb - b;
if (c % gcd) {
cout << "-1\n";
return 0;
}
lcm = a / gcd * aa;
b = ((__int128)c/gcd*x*a + b) % lcm;
a = lcm;
}
cout << (b + a) % a << "\n";
}

浙公网安备 33010602011771号