1. 程式人生 > 實用技巧 >Java Web學習筆記(二)

Java Web學習筆記(二)

給定\(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;
}