中国剩余定理(Chinese Remainder Theorem, CRT)是数论中的一个重要定理,用于解决一组同余方程的问题。它在密码学、计算机科学等领域有广泛应用。
定理内容
给定一组两两互质的正整数 n 1 , n 2 , … , n k n_1, n_2, \dots, n_k n1,n2,…,nk,以及任意整数 a 1 , a 2 , … , a k a_1, a_2, \dots, a_k a1,a2,…,ak,存在一个整数 x x x,满足以下同余方程组:
{ x ≡ a 1 ( m o d n 1 ) x ≡ a 2 ( m o d n 2 ) ⋮ x ≡ a k ( m o d n k ) \begin{cases} x \equiv a_1 \pmod{n_1} \\ x \equiv a_2 \pmod{n_2} \\ \vdots \\ x \equiv a_k \pmod{n_k} \end{cases} ⎩ ⎨ ⎧x≡a1(modn1)x≡a2(modn2)⋮x≡ak(modnk)
并且,这个解 x x x 在模 N = n 1 n 2 … n k N = n_1 n_2 \dots n_k N=n1n2…nk 下是唯一的。
证明
- 存在性:
- 由于 n 1 , n 2 , … , n k n_1, n_2, \dots, n_k n1,n2,…,nk 两两互质, N = n 1 n 2 … n k N = n_1 n_2 \dots n_k N=n1n2…nk。
- 对于每个 i i i,计算 N i = N / n i N_i = N / n_i Ni=N/ni。由于 n i n_i ni 与 N i N_i Ni 互质,存在整数 m i m_i mi 使得 m i N i ≡ 1 ( m o d n i ) m_i N_i \equiv 1 \pmod{n_i} miNi≡1(modni)。
- 令 x = ∑ i = 1 k a i m i N i x = \sum_{i=1}^k a_i m_i N_i x=∑i=1kaimiNi。对于每个 i i i, x ≡ a i m i N i ≡ a i ⋅ 1 ≡ a i ( m o d n i ) x \equiv a_i m_i N_i \equiv a_i \cdot 1 \equiv a_i \pmod{n_i} x≡aimiNi≡ai⋅1≡ai(modni),因此 x x x 是方程组的解。
- 唯一性:
- 假设 x x x 和 y y y 都是方程组的解,则 x ≡ y ( m o d n i ) x \equiv y \pmod{n_i} x≡y(modni) 对所有 i i i 成立。
- 由于 n i n_i ni 两两互质, x ≡ y ( m o d N ) x \equiv y \pmod{N} x≡y(modN),即 x x x 和 y y y 在模 N N N 下相同。
中国剩余定理通过将大模数问题分解为小模数问题,简化了计算过程。
实现
扩展欧几里得算法求逆元
乘法逆元是数论中的一个重要概念。给定整数 a a a 和模数 m m m,如果存在整数 x x x 使得 a x ≡ 1 ( m o d m ) a x \equiv 1 \pmod{m} ax≡1(modm),则称 x x x 是 a a a 在模 m m m 下的乘法逆元,记作 a − 1 a^{-1} a−1。
- 使用欧几里得算法求 gcd ( a , m ) \gcd(a, m) gcd(a,m)。
- 如果 gcd ( a , m ) ≠ 1 \gcd(a, m) \neq 1 gcd(a,m)=1,则 a a a 在模 m m m 下没有逆元。
- 如果 gcd ( a , m ) = 1 \gcd(a, m) = 1 gcd(a,m)=1,则通过扩展欧几里得算法求出 x x x 和 y y y,使得:
a x + m y = 1 a x + m y = 1 ax+my=1
此时, x x x 就是 a a a 在模 m m m 下的逆元。
代码
void exgcd(int a,int b,int &X,int &Y){
if(!b){
X=1,Y=0;return ;}
exgcd(b,a%b,X,Y);
int t=X;
X=Y;
Y=t-a/b*X;
}
完整代码
#include<bits/stdc++.h>
#define int __int128
using namespace std;
int n,m[11],r[11],c,M=1,x,y,ans;
void exgcd(int a,int b,int &X,int &Y){
if(!b){
X=1,Y=0;return ;}
exgcd(b,a%b,X,Y);
int t=X;
X=Y;
Y=t-a/b*X;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&m[i],&r[i]);
M*=m[i];
}
for(int i=1;i<=n;i++){
c=M/m[i];
exgcd(c,m[i],x,y);
ans=((ans+c*x*r[i])%M+M)%M;
}
printf("%lld",ans);
return 0;
}