中国剩余定理 CRT

ooliver OI

中国剩余定理 CRT

P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

中国剩余定理(CRT)证明

定理陈述

对于一组互质的正整数 m1,m2,[PARSE ERROR: Undefined("Command(\"dots\")")],mk,以及任意整数 a1,a2,[PARSE ERROR: Undefined("Command(\"dots\")")],ak,同余方程组:

\begin{cases} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \vdots \\ x \equiv a_k \pmod{m_k} \end{cases}

在模 M=m1m2⋯mk 下有唯一解。

证明步骤

构造解的存在性

  • 定义总模数: M=m1m2⋯mk Mi=Mmi(i=1,2,[PARSE ERROR: Undefined("Command(\"dots\")")],k)

  • 求逆元: 由于 mi 两两互质,所以 Mi 与 mi 互质: [PARSE ERROR: Undefined("Command(\"gcd\")")](Mi,mi)=1 因此存在整数 ti 使得: Miti≡1[PARSE ERROR: Undefined("Command(\"pmod\")")]mi 这里的 ti 是 Mi 在模 mi 下的乘法逆元。

  • 构造解: 令 x=∑i=1kaiMiti

  • 验证解: 对于任意 j∈{1,2,[PARSE ERROR: Undefined("Command(\"dots\")")],k}:

  • 当 i≠j 时,Mi 包含因子 mj,所以 Mi≡0[PARSE ERROR: Undefined("Command(\"pmod\")")]mj

  • 当 i=j 时,Mjtj≡1[PARSE ERROR: Undefined("Command(\"pmod\")")]mj 因此: x≡aj·1+∑i≠jai·0≡aj[PARSE ERROR: Undefined("Command(\"pmod\")")]mj

证明解的唯一性(在模 M 下)

假设存在两个解 x1 和 x2,都满足同余方程组。

那么对于每个 i: x1≡x2≡ai[PARSE ERROR: Undefined("Command(\"pmod\")")]mi 即: mi∣(x1-x2)∀i

由于 mi 两两互质,它们的乘积也整除 x1-x2: M∣(x1-x2) 所以: x1≡x2[PARSE ERROR: Undefined("Command(\"pmod\")")]M

代码:

#include
using namespace std;
#define int long long
const int N=12;
int n,m=1,x,y,ans;
int a[N],b[N];

int qmul(int a,int b,int mod){
    int res=0;
    a%=mod;
    while(b){
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}

void exgcd(int A,int B,int &x,int &y){
    if(B==0){x=1;y=0;return;}
    exgcd(B,A%B,y,x);
    y-=A/B*x;
}

signed main(){
    cin>>n;
    for(int i=1;i>a[i]>>b[i];
        m*=a[i];
    }
    for(int i=1;i
`#include
using namespace std;
#define int long long
const int N=12;
int n,m=1,x,y,ans;
int a[N],b[N];

int qmul(int a,int b,int mod){
    int res=0;
    a%=mod;
    while(b){
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}

void exgcd(int A,int B,int &x,int &y){
    if(B==0){x=1;y=0;return;}
    exgcd(B,A%B,y,x);
    y-=A/B*x;
}

signed main(){
    cin>>n;
    for(int i=1;i
        cin>>a[i]>>b[i];
        m*=a[i];
    }
    for(int i=1;i
        int c=m/a[i];
        exgcd(c,a[i],x,y);
        x=(x%a[i]+a[i])%a[i];
        int t=qmul(b[i],c,m);
        t=qmul(t,x,m);
        ans=(ans+t)%m;
    }
    cout
    return 0;
}`

P4777 【模板】扩展中国剩余定理(EXCRT)

问题描述

求解模数不一定互质的同余方程组: \begin{cases} x \equiv b_1 \pmod{a_1} \\ x \equiv b_2 \pmod{a_2} \\ \vdots \\ x \equiv b_n \pmod{a_n} \end{cases}

核心思想:合并两个方程

方程等价形式

\begin{cases} x = a_1 k_1 + b_1 \\ x = a_2 k_2 + b_2 \end{cases}

联立方程

a1k1-a2k2=b2-b1

解的存在条件

[PARSE ERROR: Undefined("Command(\"gcd\")")](a1,a2)∣(b2-b1)

令 g=[PARSE ERROR: Undefined("Command(\"gcd\")")](a1,a2)。

求解特解

用扩展欧几里得算法求: a1X+a2Y=g

调整得: k1′=X·b2-b1g

通解形式

k1=k1′+a2gt(t∈Z)

代回求 x

\begin{aligned} x &= a_1 k_1 + b_1 \\ &= a_1 \left(k_1' + \frac{a_2}{g} t\right) + b_1 \\ &= \frac{a_1 a_2}{g} t + \left(a_1 k_1' + b_1\right) \end{aligned}

合并结果

令: \begin{aligned} A' &= \mathrm{lcm}(a_1, a_2) = \frac{a_1 a_2}{g} \\ B' &= a_1 k_1' + b_1 \end{aligned}

则: x≡B′[PARSE ERROR: Undefined("Command(\"pmod\")")]A′

时间复杂度

每次合并需要一次扩展欧几里得算法,时间复杂度 O(logmin(A,ai))。 总时间复杂度 O(nlogM),其中 M=max(ai)。

代码

#include
using namespace std;
#define int __int128

int n;
int a1,a2,b1,b2,x,y;

inline __int128 read(){
	__int128 x=0,f=1;
	char ch=getchar();
	while(ch'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch9) write(x/10);
	putchar(x%10+'0');
}

void exgcd(int A,int B,int &x,int &y){
	if(B==0){x=1;y=0;return;}
	exgcd(B,A%B,y,x);
	y-=A/B*x;
}

signed main(){
	n=read(),a1=read(),b1=read();
	for(int i=2;i
`#include
using namespace std;
#define int __int128

int n;
int a1,a2,b1,b2,x,y;

inline __int128 read(){
	__int128 x=0,f=1;
	char ch=getchar();
	while(ch'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch'9') x=x*10+ch-'0',ch=getchar();
	return x*f;
}

inline void write(__int128 x){
	if(x0){
		putchar('-');
		x=-x;
	}
	if(x>9) write(x/10);
	putchar(x%10+'0');
}

void exgcd(int A,int B,int &x,int &y){
	if(B==0){x=1;y=0;return;}
	exgcd(B,A%B,y,x);
	y-=A/B*x;
}

signed main(){
	n=read(),a1=read(),b1=read();
	for(int i=2;i
		a2=read(),b2=read();
		int g=__gcd(a1,a2);
		exgcd(a1,a2,x,y);
		x*=(b2-b1)/g;
		x=(x%(a2/g)+(a2/g))%(a2/g);
		int A1=a1,B1=b1;
		a1=A1*a2/__gcd(A1,a2);
		b1=A1*x+B1;
	}
	write((b1%a1+a1)%a1);
	return 0;
}`