中國剩餘定理及其變形
阿新 • • 發佈:2021-12-21
中國剩餘定理
解方程組:
\[x \equiv a_1 \pmod {m_1} \\ x \equiv a_2 \pmod {m_2} \\ ... \\ x \equiv a_k \pmod {m_k} \\ 其中m_1, m_2...m_k兩兩互質 \]記
\[M = \prod^{k}_{i = 1} m_i \\ M_i = \frac{M}{m _ i} \]公式解:
\[x = \sum^{k}_{i = 1} a_iM_iM_i^{-1} \]求\(M_i^{-1}\):
模\(n\)意義下,非零整數\(x\)的逆元存在的充要條件是:\(x\)與\(n\)互質
因為\(M_i\)
求逆元:應用拓展歐幾里得演算法,解方程
\[M_i \cdot M_i^{-1} \equiv 1 \pmod {m_i} \]變形(AcWing 204. 表達整數的奇怪方式)
解方程組:求一個最小的非負整數\(x\)滿足
\[x \equiv a_1 \pmod {m_1} \\ x \equiv a_2 \pmod {m_2} \\ ... \\ x \equiv a_k \pmod {m_k} \\ 其中m_1, m_2...m_k不一定兩兩互質 \]策略:兩兩合併(我也不知道是怎麼想到的==)
先考慮前兩個方程,將其轉化為等式形式
將\(通解k\)代入(1)得:
\[\begin{align*} x & = a_1k_1 + m_1 + k_1 \frac{a_1 a_2}{d}\\ & =a_1k_1 + m_1 + k_1[a_1,a_2] \\ & = x_0 + k_1a' \end{align*} \]但是在實際程式執行過程中,由於資料範圍比較極限,所以過程中要縮小\(k_1\)
這個\(k'\)是同時滿足(1),(2)的,在程式碼中我們把\(k_1\)替換為\(k'\)。
同樣的\(x_0\)也是同時滿足(1),(2)的,所以滿足方程
\[x = x_0 + k_1a' \]的解就同時是(1),(2)的解
以上,我們就得到了一條新的方程,它的解同時滿足(1)和(2),於是我們只要合併n - 1次就能得到一條解滿足所有方程的方程
\[x' = x_0' + k'a'' \\ 其中x_0',a''已知 \]題目要使\(x\)最小且非負,我們只需把\(x'\)中的因子\(a''\)都除掉即可
\[答案 = (x_0' \bmod a'' + a'') \bmod a'' \]p.s.上述很多過程在程式中體現為重複的賦值
程式碼
#include<iostream>
using namespace std;
typedef long long 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(void){
int n;
cin >> n;
bool flag = 1; //記錄是否無解
ll a1, m1;
cin >> a1 >> m1;
for (int i = 0; i < n - 1; i ++ ){ //每次把一個新的方程合併到現有方程之中
ll a2, m2, k1, k2;
cin >> a2 >> m2;
ll d = exgcd(a1, a2, k1, k2);
if ((m2 - m1) % d != 0){
flag = 0;
break;
} //求了之後要用的其實只有k1
k1 *= (m2 - m1) / d; //用拓歐記得要放大
ll t = a2 / d; //由於資料範圍比較極限,過程中要縮小k,使k變成通解當中的最小正整數解
k1 = (k1 % t + t) % t;
m1 = a1 * k1 + m1; //重新整理m
a1 = abs(a1 / d * a2); //重新整理a1,使其變成a1,a2的最小公倍數,除法在前放溢位
}
if (flag){
cout << (m1 % a1 + a1) % a1 << endl;
}
else
puts("-1");
return 0;
}