[中国剩余定理]【学习笔记】
求解同余方程请看 http://www.cnblogs.com/candy99/p/5765986.html
[2017-02-14 19:05]
[2017-03-23 update]:看组合数学的时候发现了一个证明,记一下;改成了markdown
Chinese Remainder Theorem 中国剩余定理
定理
考虑两个方程的情况
对于n个整数\(a,\ m+a,\ 2m+a\ , ... ,\ (n-1)m+a\), 用反证法可以证明,每个数\(\mod n\)都有都有不同的余数
根据鸽巢原理,\(b\)必定作为余数出现了且只出现一次,因此这\(n\)个数中存在唯一的\(x\)满足上式
应用
求解同余方程组
其中 $ (m_i,m_j)=1,\ 1\le i < j\le n $
**直接构造解:**
\(M =\prod_{i=1}^{m} m_i\)
首先明确\(x \mod M\)有唯一解
$ M=m_1 * m_2 * m_3 * ... * m_k $
$ x=(\sum\limits_{i=1}^k a_i* {M\over m_i} *inv({M\over 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}\)意义下了
所以合并后方程为 $x\equiv m_1*t_1 + a_1\pmod {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\n",a1);
}
}