Part. 0 前置知識
- 擴展歐幾里得算法;
- 模運算相關知識。
Part. 1 中國剩餘定理
中國剩餘定理是用於求解形如:
的同餘方程組,其中要求兩兩互質。
考慮用構造的方法去解決這個問題。
令。再令爲同餘方程的最小非負整數解,那麼就有特解:
它的通解形式爲:
特別的,這個方程的最小非負整數解爲。
接下來說明這個特解是成立的:
【這個部分參考自niiick的博客】我實在太菜了連這東西都推不出來了QAQ…
因爲是除了之外的所有的的倍數,所以,,。
由於,我們乘上得到:。
故將這個特解代入到每個方程裏面去都是成立的。
Part. 2 擴展中國剩餘定理
擴展中國剩餘定理是用於求解形如:
的同餘方程組,其中對沒有任何要求。
我們假設已經將前面個同餘方程合併了,並得到一個特解。
令,考慮與第個方程合併。我們通過同餘式的性質可以將這兩個方程等價轉化爲:
簡單地讓兩個式子相等起來:
移一下項:
然後套用擴展歐幾里得算法解這個方程:
若則整個方程組無解。
否則我們就選取的最小非負整數解(爲了避免精度爆炸),這時我們得到將兩個方程合併後的新的特解。
於是遞推計算下去就可以了。
特別注意: 在實際應用中,爲避免精度爆炸,我們可以等價地令。
Part. 3 模板題
中國剩餘定理
簡單分析題意可以得到:
將這些化成同餘方程組的形式:
移項過去:
又由於題目已經給定了是兩兩互質的。我們不難發現這是一個符合中國剩餘定理的同餘方程組,於是直接套板。
直接套用模板是不夠的,注意題目中輸入的數字可能是負數,且還要乘炸long long
。
細節還挺多的。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Maxn = 10;
int N;
ll A[Maxn + 5], M[Maxn + 5];
ll ExGCD(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
ll g = ExGCD(b, a % b, y, x);
y -= a / b * x;
return g;
}
ll SlowMul(ll x, ll y, ll mod) {
ll ret = 0;
while(y) {
if(y & 1) ret = (ret + x) % mod;
x = (x + x) % mod;
y >>= 1;
}
return ret;
}
ll CRT() {
ll m = 1, x0 = 0;
for(int i = 1; i <= N; i++)
A[i] = (A[i] % M[i] + M[i]) % M[i];
for(int i = 1; i <= N; i++)
m *= M[i];
for(int i = 1; i <= N; i++) {
ll x, y;
ExGCD(m / M[i], M[i], x, y);
x = (x % M[i] + M[i]) % M[i];
x0 = (x0 + SlowMul(x * (m / M[i]), A[i], m)) % m;
}
return x0;
}
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d", &N);
for(int i = 1; i <= N; i++)
scanf("%lld", &A[i]);
for(int i = 1; i <= N; i++)
scanf("%lld", &M[i]);
printf("%lld\n", CRT());
return 0;
}
擴展中國剩餘定理
題目來源:洛谷-P4777-【模板】擴展中國剩餘定理(EXCRT)
由於這道題要炸long long
,故程序中用了龜速乘來解決這個問題。其實還可以用更爲高端的快速乘,但我太菜了 不想打 。。。
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Maxn = 1e5;
int N;
ll A[Maxn + 5], M[Maxn + 5];
ll ExGCD(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
ll g = ExGCD(b, a % b, y, x);
y -= a / b * x;
return g;
}
inline ll SlowMul(ll x, ll y, ll mod) {
if(y < 0) x = -x, y = -y;
ll ret = 0;
while(y) {
if(y & 1) ret = (ret + x) % mod;
x = (x + x) % mod;
y >>= 1;
}
return ret;
}
ll ExCRT() {
ll m = M[1], x0 = A[1];
for(int i = 2; i <= N; i++) {
ll x, y;
ll g = ExGCD(m, M[i], x, y);
if((A[i] - x0) % g) return -1;
ll tmp = M[i] / g;
x = (SlowMul(x, (A[i] - x0) / g, tmp)+ tmp) % tmp;
x0 += m * x;
m = m / g * M[i], x0 %= m;
}
return x0;
}
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d", &N);
for(int i = 1; i <= N; i++)
scanf("%lld %lld", &M[i], &A[i]);
printf("%lld\n", ExCRT());
return 0;
}