求最小的x满足线性同余方程组
x 同余 a1(mod m1)
x 同余 a2(mod m2)
……
x 同余 an(mod mn)
(题中m1,m2……mn两两互质)
-
证明:
-
先讨论前两个线性同余方程组
-
x=k1 ∗ \ast ∗ m1+a1,x=k2 ∗ \ast ∗ m2+a2
-
只要满足存在k1,k2使得(m1,m2) | a2 - a1就一定有解,设该最大公因数为d
-
有性质:(k1,k2的解系)
-
k1=k1+k ∗ m2 ÷ \div ÷ d
-
k2=k2+k ∗ m1 ÷ \div ÷ d
-
(k为任意整数k1,k2仍满足关系)
-
证明:
-
将新的k1,k2带入式子得:
-
(k1+k ∗ m2 ÷ \div ÷ d) ∗ m1 + (k2+k ∗ m1 ÷ \div ÷ d) ∗ (-m2)
-
化简后上式等于a2 - a1故证出
-
要找一个最小的非负整数解,我们只需要让
-
k1=k1 mod abs (m2 ÷ \div ÷ d)
-
k2=k2 mod abs (m1 ÷ \div ÷ d)
-
即可找到当前最小的k1,k2的解,即此时的k=0
-
新的x为:
-
x=(k1+k ∗ m2 ÷ \div ÷ d) ∗ m1+a1
-
x=k1 ∗ m1+a1 + k ∗ lcm(m1,m2)
-
这里,k都为0了,为什么还要算呢
-
因为这只是前两个式子得最小k,有可能遇到下一个式子后面被迫扩大
-
a0=lcm(m1,m2),m0=k1∗m1+a1
-
那么:x=k∗m0+a0
-
这个形式与一开始我们分解的形式是不是特别像呢?
-
没错!假设之后又来了一个a3,m3
-
我们只需要继续找:
-
x=k∗m0+a0=k3∗(−m3)+a3,那么问题又回到了第一步
-
我们的做法相当于每次考虑合并两个式子,将这n个式子合并n−1次后变为一个式子。最后剩下的式子就满足我们的答案。
-
代码实现
#include<iostream>
using namespace std;
typedef long long LL;//数据范围比较大,所以用LL来存储
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
x=1,y=0;
return a;
}
LL d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
int main()
{
int n;
LL a1,m1;
cin>>n>>a1>>m1;
LL x=0;
for(int i=1;i<n;i++)
{
LL a2,m2;
cin>>a2>>m2;
LL k1,k2;
LL d=exgcd(a1,a2,k1,k2);
if((m2-m1)%d)
{
x=-1;
break;
}
k1*=(m2-m1)/d;
//因为此时k1是k1*a1+k2*a2=d的解,所以要乘上(m2-m1)/d的倍数大小
LL t=abs(a2/d);
k1=(k1%t+t)%t;
//数据比较极端,所以只求k的最小正整数解
m1=k1*a1+m1;
//m1在被赋值之后的值为当前"x"的值,此时赋值是为了方便下一轮的继续使用
a1=abs(a1*a2/d);
//循环结束时a1的值为当前所有的a1,a2,……an中的最小公倍数
}
if(x!=-1)
x=(m1%a1+a1)%a1;
//当循环结束时,此时的值应该与最小公倍数取模,以求得最小正整数解
printf("%lld\n",x);
return 0;
}