[中国剩余定理]【学习笔记】

求解同余方程请看 http://www.cnblogs.com/candy99/p/5765986.html
[2017-02-14 19:05]
[2017-03-23 update]:看组合数学的时候发现了一个证明,记一下;改成了markdown


Chinese Remainder Theorem 中国剩余定理

定理

考虑两个方程的情况

[x equiv a pmod m \ x equiv b pmod n ]

对于n个整数(a, m+a, 2m+a , ... , (n-1)m+a), 用反证法可以证明,每个数(mod n)都有都有不同的余数
根据鸽巢原理(b)必定作为余数出现了且只出现一次,因此这(n)个数中存在唯一的(x)满足上式


应用

求解同余方程组

[xequiv a_1pmod {m_1}\ xequiv a_2pmod {m_2} \ xequiv a_3pmod {m_3}\ ...\xequiv a_npmod {m_n} ]

其中 $ (m_i,m_j)=1, 1le i < jle n $


**直接构造解:**

(M =prod_{i=1}^{m} m_i)

首先明确(x mod M)有唯一解

$ M=m_1 * m_2 * m_3 * ... * m_k $

$ x=(sumlimits_{i=1}^k a_i* {Mover m_i} *inv({Mover m_i},m_i))mod M $

证明:
(x mod m_i)时 第(i)个式子因为有乘逆元所以结果是ai*1,其他式子的(M)可以整除(m_i)所以都是(0),方程组成立


代码


ll m[N],a[N],M=1;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(b==0) d=a,x=1,y=0;
	else exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}
ll Inv(ll a,ll p){
	ll d,x,y;
	exgcd(a,p,d,x,y);
	return d==1?(x+p)%p:-1;
}
ll CRT(int n,ll *a,ll *m){
	ll x=0;
	for(int i=1;i<=n;i++){
		ll w=M/m[i];
		x=(x+a[i]*w%M*Inv(w,m[i]))%M;
	}
	return x;
}



合并同余方程

如果不满足m两两互质,我们采用合并同余方程的方法将方程两两合并

两个方程可以写成$ x=m_1 * t_1+a_1=m_2 * t_2+a_2 $

变形 $ m_1 * t_1-m_2 * t_2=a_2-a_1 $

线性不定方程的形式

$ d=gcd(m_1,m_2) $ 有解满足 $d| (a_2-a_1) $

(exgcd)后乘(frac{a2-a1}{d})解出(t_1)当前是(mod frac{m_2}{d})意义下

代入(x = m_1*t_1+a_1)中,就变成(mod frac{m_1*m_2}{d})意义下了

所以合并后方程为 $xequiv m_1*t_1 + a_1pmod {lcm(m_1,m_2)} $

注意 (t_1)理论上是绝对值最小解,但也许是因为乘了(frac{a2-a1}{d})之后吧求最小的时候不能简单的if(t1<0) t1+=m2啦,需要模一下再变正

代码


void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(b==0) d=a,x=1,y=0;
	else exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}
int main(){
	freopen("in","r",stdin);
	while(scanf("%lld",&n)!=EOF){
		int flag=0;
		m1=read();a1=read();n--;
		while(n--){
			m2=read();a2=read();
			if(flag) continue;
			ll d,t1,t2;
			exgcd(m1,m2,d,t1,t2);
			if((a2-a1)%d){flag=1;continue;}
			t1*=(a2-a1)/d;
			m2/=d;
			t1=(t1%m2+m2)%m2;
			a1=a1+m1*t1;
			m1*=m2;
		}
		if(flag) puts("-1");
		else printf("%lld
",a1);
	}
}


原文地址:https://www.cnblogs.com/candy99/p/6603455.html