Java Web學習筆記(二)
阿新 • • 發佈:2020-12-27
給定\(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)\)
證明好像非常潦草,但其實證明並不重要。
優化手段
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; }