抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

问题

中国剩余定理用来解决如下问题:

求关于\(x\)的方程组

\[\begin{cases} x \equiv a_1 \pmod{p_1} \\ x \equiv a_2 \pmod{p_2} \\ \cdots \\ x \equiv a_n\pmod{p_n}\end{cases}\]

的最小非负整数解。

其中\(p_i\)两两互质。

前置技能

扩展欧几里得求乘法逆元

求解

\(M=\prod_{i=1}^{n} p_i,d_i=\frac{M}{p_i}\),即\(d_i\)表示除\(p_i\)外所有\(p\)的乘积。

\(inv_i\)表示\(d_i\)在模\(p_i\)域下的逆元(因为\(p\)两两互质,所以\(d_i\)一定与\(p_i\)互质,所以一定存在逆元),即\(inv_i \times d_i \equiv 1 \pmod{p_i}\)。记\(x_i=inv_i\times d_i\),则:

  1. 因为\(d_i\)能被除\(p_i\)外的所有\(p\)整除,所以\(x_i\)一定能被除\(p_i\)外的所有\(p\)整除,所以\(x_ia_i\)一定能被除\(p_i\)外的所有\(p\)整除。
  2. \(x_i\equiv 1 \pmod{p_i}\),所以\(a_ix_i\equiv a_i \pmod{p_i}\)

根据以上两条推论可得,答案为\(\left( \sum_{i=1}^{n} a_ix_i \right) \bmod M\)。因为对于第\(i\)个方程,根据推论1,对于所有的\(j\ne i\)\(a_jx_j\)不会对该方程组产生贡献,即\(a_jx_j\equiv 0 \pmod{p_i} (j\ne i)\)。只有\(a_ix_i\)会对第\(i\)个方程产生贡献,且根据推论2,恰好会产生\(a_i\)的贡献。又因为对于所有\(i\)\(M\equiv 0\pmod{p_i}\),所以答案加上或减去若干个\(M\)都满足方程组。求的是最小非负整数解,所以答案\(\bmod M\)

代码

中国剩余定理模板题:[TJOI2009]猜数字

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
#include <cstdio>
long long exgcd(long long a, long long b, long long &x, long long &y){
if (!b) return x = 1, y = 0, a;
long long x0, y0, g = exgcd(b, a % b, x0, y0);
return x = y0, y = x0 - a / b * y0, g;
}
int n, a[15], p[15];
long long inv(long long a, long long p){
long long x, y, g = exgcd(a, p, x, y);
if (g != 1) return -1;
return (x % p + p) % p;
}
long long qmul(long long a, long long b, long long p){
long long s = 0;
for (a %= p, b %= p; b; b >>= 1, a = (a + a) % p) b & 1 ? s = (s + a) % p : 0;
return s;
}
int main(){
scanf("%d", &n);
for (register int i = 1; i <= n; ++i) scanf("%d", a + i);
for (register int i = 1; i <= n; ++i) scanf("%d", p + i);
for (register int i = 1; i <= n; ++i) a[i] = (a[i] % p[i] + p[i]) % p[i];
long long m = 1, ans = 0;
for (register int i = 1; i <= n; ++i) m = m * p[i];
for (register int i = 1; i <= n; ++i){
long long d = m / p[i], d_ = inv(d, p[i]);
(ans += qmul(qmul(d, d_, m), a[i], m)) %= m;
}
printf("%lld", ans);
}

评论