题目传送门
首先看第一个同余方程, x ≡ m 1 ( m o d a 1 ) x\equiv m_1(mod \ a_1) x≡m1(mod a1)可以化为 1 × x + a 1 × y = m 1 1\times x+a_1 \times y=m_1 1×x+a1×y=m1
可以根据扩展欧几里得算法求出一个 x x x的可行解,如果是负数
while (x < 0){
x = (x + a[1]) % a[1];
}
如果已经有了前k个同余方程的解为 x x x,前k+1个同余方程的解也可以求得
设 L C M k LCM_k LCMk是 l c m ( a 1 , a 2 , . . . , a k ) lcm(a_1,a_2,...,a_k) lcm(a1,a2,...,ak)
前k+1个同余方程的解设为 y y y,则一定满足 y = x + L C M k × n y=x+LCM_k\times n y=x+LCMk×n( n n n为整数),
同时 1 × y + a k + 1 × q = m k + 1 1\times y+a_{k+1}\times q=m_{k+1} 1×y+ak+1×q=mk+1
即 x + L C M k × n + a k + 1 × q = m k + 1 x+LCM_k\times n+a_{k+1}\times q=m_{k+1} x+LCMk×n+ak+1×q=mk+1
L C M k × n + a k + 1 × q = m k + 1 − x LCM_k\times n+a_{k+1}\times q=m_{k+1}-x LCMk×n+ak+1×q=mk+1−x
用扩展欧几里得算法检测是否有解,如果该方程无解,那么就不存在答案
若方程有解,则 y = x + L C M k × n y=x+LCM_k\times n y=x+LCMk×n
求解完第一个后,依据上述过程求解后续的解即可
求解过程中,如果扩展欧几里得算法求得了一个负数值,转变为正数,即与模数相加取模即可
最后程序会得到一个值,是一个满足条件的可行解,但是不一定最小,只需要对全部的数字的最小公倍数 L C M n LCM_n LCMn取模即可
假设最小可行解是 a n s ans ans,则通解是 a n s + L C M n × N ans+LCM_n\times N ans+LCMn×N( N N N为整数),通解对每一个 a i a_i ai取模时,可以分为两部分,第一部分取模后显然满足条件,而第二部分的 L C M n × N LCM_n\times N LCMn×N,因为 L C M n LCM_n LCMn是 a i a_i ai的倍数,因此取模后为0,所以最后结果依然满足条件,因此上述表达式为通解。
#include <bits/stdc++.h>
template <typename T>
void exgcd(T a, T b, T& x, T& y){
if (b == 0){
x = 1;
y = 0;
return;
}
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
template<typename T>
T gcd(T a, T b){
return b == 0 ? a : gcd(b, a % b);
}
template<typename T>
T lcm(T a, T b){
return a / gcd(a, b) * b;
}
long long a[30], m[30], n;
long long x, y, res, M;
int main(){
// freopen("in.in", "r", stdin);
// freopen("out.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin >> n;
for (int i = 1; i <= n; i++){
std::cin >> a[i] >> m[i];
}
M = a[1];
exgcd(1ll, a[1], res, y);
res = (res + a[1]) % a[1];
res *= m[1] / gcd(1ll, a[1]);
int i;
for (i = 2; i <= n; i++){
long long temp = m[i] - res;
while (temp < 0)temp = (temp + a[i]) % a[i];
temp %= a[i];
long long _gcd = gcd(M, a[i]);
if (temp % _gcd){
break;
}
exgcd(M, a[i], x, y);
x = (x + a[i]) % a[i];
x = (x * (temp / _gcd)) % a[i];
res += (x * M);
M = lcm(M, a[i]);
}
if (i <= n){
std::cout << "-1\n";
}
else std::cout << res % M << "\n";
return 0;
}