给定(n)个形如(x equiv a_i(mod b_i))的同余方程,求出最小正整数解。
证明
首先对于两个同余方程:
(x equiv a_1(mod b_1))
(x equiv a_2(mod b_2))
我们可以进行一次“合并”操作,将上面两个方程合并为一个同余方程:
(x equiv a_3(mod b_3))
那么如何合并呢?首先很容易可以求出(b_3=lcm(b_1,b_2)),然后我们注意到(x equiv a_1(mod b_1)),那么(x)可以写成(k*a_1+b_1)的形式,然后枚举(k),直到满足(k*a_1+b_1 equiv a_2(mod b_2)),此时跳出循环。而注意到当(k)枚举到(a_2)的时候,再往后枚举就没有意义了,就会重复,所以如果此时还没有找到符合条件的解,就无解,退出循环即可。
证明好像非常潦草,但其实证明并不重要。
优化手段
1.由于我们要在循环里枚举(a_2),所以一开始先判断(a_1)和(a_2)的大小关系,如果(a_1)要更小,那么交换(a_1),(a_2)和(b_1),(b_2)。
2.可以将(a_i)从大到小排序。(不懂为什么)
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
ll n,ans1,ans2;
ll a[110],b[110];
ll gcd(ll x,ll y){
if(y==0) return x;
else return gcd(y,x%y);
}
ll lcm(ll x,ll y){
return x/gcd(x,y)*y;
}
void merge(ll a1,ll b1,ll a2,ll b2,ll &a3,ll &b3){//合并两个同余方程
a3=lcm(a1,a2);
if(b1<b2){
swap(a1,a2);
swap(b1,b2);
}
for(int i=0;i<a2;i++){
if((b1+i*a1)%a2==b2){
b3=(b1+i*a1)%a3;
}
}
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i],&b[i]);
}
ans1=1,ans2=0;
for(int i=1;i<=n;i++){
merge(ans1,ans2,a[i],b[i],ans1,ans2);
}
printf("%lld
",ans2);
return 0;
}