一.算法描述
情况1:mi两两互质
中国剩余定理是指解决求一次同余式组问题的方法,求一个最小的非负整数 ,满足
其中两两互质。
我们先回忆逆元的定义:对于任意满足:
这样,我们称为的模的乘法逆元,记作.
这样,我们可以得到:
这样,我们就可以给出的一个通式:
其中:
下给出证明:
我们只需要证明,对于任意的,会满足即可。
而对于中的任意一项:
1.若,那么,而,所以:
2.若,那么对于中,由于,这样一定是包括了的,所以:
综上,对于任意的,。
下来,我们需要去求出关于的逆元:
事实上,在前面扩展欧几里得算法中,我们已经可以求解:
那我们现在要求而已:
但是我们要注意和是互质的,因而.
这样我们就可以用扩展欧几里得算法求出。
情况二:mi不能保证两两互质
在mi不能保证两两互质的情况下,那么中国剩余定理就不能使用了,因为你不能保证每一个都存在逆元。
这时候我们就采用另一种方法:
首先我们考虑前面的两个式子:
那我们可以把这两个式子写成:
这样:
同样的,我们令,由于都是的倍数,那么上式有解等价于可以整除于。
这样,我们可以写出的一个通解:
为什么捏?这是因为
这样,我们将(*)式整理一下:
这样,我们就把两个式子合并成了一个,其中,表示的最小公倍数。
因而,在新的式子中,把看成新的式子的,看成是,即:
无解的条件即为:
基本步骤解决了,下面有几个问题需要讨论:
k1=k1*(a2-a1)/d; k1 = (k1 % (m2/d) + m2/d) % (m2/d);
这里其实k1有可能是负数的(因为一般扩展欧几里得算法不止一个解叭),那我觉得如果k1是负数的话,我们的目的是让m1k1对m1m2取余,这样也就是k对m2/d取余,由于k有可能是负数,一般步骤就是先对x取余,然后加上x,再对x取余。
最后在得到x的时候,同样让a对m取余,以防x是负数,加上:
x = (a1 % m1 + m1) % m1;
二.具体代码
#include<iostream>
using namespace std;
typedef pair<long,long> II;
II exgcb(long a,long b){
if(b==0) return {1,0};
else{
II tmp=exgcb(b,a%b);
return {tmp.second,tmp.first-a/b*(tmp.second)};
}
}
int main(){
int n;
cin>>n;
long long a1,m1;
cin>>m1>>a1;
bool flag=true;
long long x=0;
for(int i=1;i<=n-1;i++){
long long a2,m2;
cin>>m2>>a2;
II tmp=exgcb(m1,m2);
long long d=m1*tmp.first+m2*tmp.second;
if((a2-a1)%d){//d有可能是负的
flag=false;
break;
}
else{
long long k1=tmp.first;
k1=k1*(a2-a1)/d;
k1 = (k1 % (m2/d) + m2/d) % (m2/d);
x=m1*k1+a1;
a1=x%(m1/d*m2);
m1=m1/d*m2;
}
}
if(flag){
x = (a1 % m1 + m1) % m1;
cout<<x;
}
else cout<<-1;
}