# 中国剩余定理(CRT)
中国剩余定理是用来解决如下问题的:
注意:模数必须互质。
# 解法
我们设
相当于是 在模 意义下的逆元。
此时我们可以构造出一组解:
进而推出任意解:
# 证明
根据数组的含义,容易得到,对于第 个同余方程,当 时,都有:
因为 中包含 。
当 时,有:
证毕.
逆元需要用到扩展欧几里得算法(exgcd)来求解,这里不再赘述,不会的左转百度一下吧QwQ
# 代码
例题:P1495 【模板】中国剩余定理(CRT)/曹冲养猪
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using namespace std;
ll n, mul = 1, ans;
ll a[15], b[15], m[15];
inline void exgcd(ll a, ll b, ll &x, ll &y){
if(!b) return x = 1, y = 0, void();
exgcd(b, a % b, x, y);
ll z = x;
x = y, y = z - (a / b) * y;
}
signed main(){
scanf("%lld", &n);
for(ll i = 1; i <= n; i++){
scanf("%lld%lld", &a[i], &b[i]);
mul *= a[i];
}
for(ll i = 1; i <= n; i++){
m[i] = mul / a[i];
ll x = 0, y = 0;
exgcd(m[i], a[i], x, y);
if(x < 0) x += a[i];
ans += b[i] * m[i] * x;
}
printf("%lld\n", ans % mul);
return 0;
}
(远古代码,有些丑陋,见谅。)
# 扩展中国剩余定理(exCRT)
注意到多了扩展,那么这个扩展在哪里呢?
普通的 CRT 要求模数必须互质,这就很恶心,exCRT 就解决了这个问题,也就是模数不互质时的解决方法。
# 解法
同样是这个式子:
模数不互质时怎么办呢?
我们考虑能否把两个同余方程合并为一个。
我们对两个方程进行讨论:
同余也可以表示为:
移项:
诶,这不就是一个 exgcd 的形式吗,所以容易判断是否有解:
- ,有解。
- 否则无解。
继续来看这个式子,设:
显然 和 是互质的,代入到原式中:
这个式子中,左边满足了 ,右边当且仅当 才有解,利用 exgcd 求出一组特解:
然后可以直接算出 :
于是:
至此,我们成功构造关于方程
的一个解,但是整个解系如何构造呢?
定理: 我们有一个特解 ,那么方程
通解为:
即
这个构造正确性是显然的。
那么接下来还有一个结论: 中只有一个解。
关于这个的证明,只需要设存在 都是解。
然后推一下式子即可得出, 都是解当且仅当 。
# 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using namespace std;
namespace IO{
inline int read(){
int x = 0;
char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 3)+ (x << 1) + ch - '0', ch = getchar();
return x;
}
template <typename T> inline void write(T x){
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const Int N = 1e5 + 10;
Int a1, b1, a2, b2, flag;
ll a[N], b[N], n;
inline Int exgcd(Int a, Int b, Int &x, Int &y){
if(!b) return x = 1, y = 0, a;
Int g = exgcd(b, a % b, x, y);
Int z = x;
x = y, y = z - (a / b) * y;
return g;e
}
inline void merge(){
Int d = b2 - b1, x, y, g;
g = exgcd(a1, a2, x, y);
Int mod = a2 / g;
if(d % g == 0){
x = ((x * d / g) % mod + mod) % mod;
b1 = x * a1 + b1;
a1 = (a1 * a2) / g;
}else flag = 1;
}
inline Int excrt(){
a1 = a[1], b1 = b[1];
for(Int i = 2; i <= n; i++){
a2 = a[i], b2 = b[i];
merge();
if(flag) return -1;
}
return b1;
}
signed main(){
n = read();
for(Int i = 1; i <= n; i++) a[i] = read(), b[i] = read();
write(excrt()), puts("");
return 0;
}