题目描述
给定 2n 个整数 a1,a2,…,an 和 m1,m2,…,mn,求一个最小的非负整数 x,满足 ∀i∈[1,n],x≡mi(mod ai)。
输入格式
第 1 行包含整数 n。
第 2…n+1 行:每 i+1 行包含两个整数 ai 和 mi,数之间用空格隔开。
输出格式
输出最小非负整数 x,如果 x 不存在,则输出 −1。
如果存在 x,则数据保证 x 一定在 64 位整数范围内。
样例
Input
2
8 7
11 9
Output
31
算法
前置知识:扩展欧几里得,裴蜀定理, 线性同余方程,中国剩余定理(CRT)
下面是链接 :
扩欧,线性同余方程
Bézout’s
中国剩余定理
如果您已经对上述算法有了解 ,OK 我们开始
中国剩余定理给出了模数两两互质的情况,而本题无此限制,所以,像推欧几里得那样,我们思考数学归纳法
设已经求出1到k - 1的解ans, Lcm = lcm(a1, a2, …
a
k
−
1
a_{k-1}
ak−1), 所以ans + i * Lcm(i∈Z)是前k - 1个方程的解集
考虑第k个方程 要求出一个x 满足 ans + x * Lcm ≡
m
k
m_k
mk (mod
a
k
a_k
ak)
<=> x * Lcm ≡
m
k
m_k
mk - ans(mod
a
k
a_k
ak)
诶,这不就是扩展欧几里得求线性同余吗,只需判断是否有解
即gcd(Lcm * x,
a
k
a_k
ak)是否整除
m
k
m_k
mk - ans)求出解或输出-1即可
哦对了,这题数据卡得很紧,并且要求最小解,要时时刻刻注意取模!!否则溢出后果不堪设想!!
到此本题完美解决。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[maxn], m[maxn];
int exgcd(int a, int b, int &x, int &y) {
if (b == 0LL) {
x = 1LL, y = 0LL;
return a;
}
int d = exgcd(b, a % b, x, y);
int z = x; x = y, y = z - a / b * y;
return d;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> m[i] >> a[i];
int ans = a[1], x = 0, y = 0, d = 0, Lcm = m[1];
for (int i = 2; i <= n; i++) {
d = exgcd(Lcm, m[i], x, y);
x = (x % m[i] + m[i]) % m[i];
if ((a[i] - ans) % d == 0) {
x = (a[i] - ans) / d * x;
x = (x % (m[i] / d) + (m[i] / d)) % (m[i] / d); // 这里一定要取模
ans = ans + x * Lcm;
Lcm = Lcm * m[i] / d;
ans = (ans % Lcm + Lcm) % Lcm;
} else {
cout << -1 << endl;
return 0;
}
}
cout << ans << endl;
return 0;
// * author: whc
}
参考文献:算法竞赛进阶指南 lyd