题目传送门

中国剩余定理(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} xa1(modn1)xa2(modn2)xak(modnk)

并且,这个解 x x x 在模 N = n 1 n 2 … n k N = n_1 n_2 \dots n_k N=n1n2nk 下是唯一的。

证明

  1. 存在性
  • 由于 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=n1n2nk
  • 对于每个 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} miNi1(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} xaimiNiai1ai(modni),因此 x x x 是方程组的解。
  1. 唯一性
  • 假设 x x x y y y 都是方程组的解,则 x ≡ y ( m o d n i ) x \equiv y \pmod{n_i} xy(modni) 对所有 i i i 成立。
  • 由于 n i n_i ni 两两互质, x ≡ y ( m o d N ) x \equiv y \pmod{N} xy(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} ax1(modm),则称 x x x a a a 在模 m m m 下的乘法逆元,记作 a − 1 a^{-1} a1

  1. 使用欧几里得算法求 gcd ⁡ ( a , m ) \gcd(a, m) gcd(a,m)
  2. 如果 gcd ⁡ ( a , m ) ≠ 1 \gcd(a, m) \neq 1 gcd(a,m)=1,则 a a a 在模 m m m 下没有逆元。
  3. 如果 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;
}