中国剩余定理

给定\(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\n",ans2);
	return 0;
}
posted @ 2020-12-27 14:57  徐明拯  阅读(60)  评论(0编辑  收藏  举报