全程学自y总
AcWing.204.表达整数的奇怪方式
给定
2
n
2n
2n 个整数
a
a
a1,
a
a
a2,…,
a
a
an 和
m
m
m1,
m
m
m2,…,
m
m
mn,求一个最小的非负整数
x
x
x,满足
∀
i
∈
[
1
,
n
]
,
x
≡
m
∀i∈[1,n],x≡m
∀i∈[1,n],x≡mi
(
m
o
d
a
(mod a
(modai
)
)
)。
输入格式
第 1 行包含整数
n
n
n。
第 2… n n n+1 行:每 i i i+1 行包含两个整数 a a ai 和 m m mi,数之间用空格隔开。
输出格式
输出最小非负整数
x
x
x,如果
x
x
x 不存在,则输出 −1。
数据范围
1
≤
a
1≤a
1≤ai
≤
231
−
1
,
0
≤
m
≤231−1,0≤m
≤231−1,0≤mi
<
a
<a
<ai
1
≤
n
≤
25
1≤n≤25
1≤n≤25
所有
m
m
mi 的最小公倍数在
64
64
64 位有符号整数范围内。
输入样例:
8 7
11 9
输出样例:
31
中国剩余定理:
以
M
=
m
M=m
M=m1
∗
m
*m
∗m2
∗
.
.
.
m
*...m
∗...mk。
以 M M Mi = = = M m i \frac{M}{m~i~} m i M。即Mi表示除了mi之外其他所有m的乘积,则Mi和mi是互质的,则我们可以求出 M M Mi m o d m modm modmi的逆元
用Mi-1表示 M M Mi m o d m modm modmi的逆元,逆元即 a ∗ x ≡ 1 ( m o d m ) a*x ≡ 1(modm) a∗x≡1(modm),即我们可以通过扩展欧几里得算法来求出逆元
则 x = a x = a x=a1 ∗ M *M ∗M1 ∗ M *M ∗M1-1 + a +a +a2 ∗ M *M ∗M2 ∗ M *M ∗M22 + + +… a a an ∗ M *M ∗Mn ∗ M *M ∗Mn-1。此式子得到的 x x x就是解
对于此道题:我们现在有很多个方程(x mod ai = mi),需要在每一步去合并方程
过程如下:
代码如下:
#include<iostream>
using namespace std;
#define ll long long
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() {
int n; cin >> n;
bool has_answer = 1; //判断是否有解
ll a1, m1;
cin >> a1 >> m1; //第一个方程的a1和m1
for (int i = 0; i < n - 1; i++) { //要合并n-1次方程
ll a2, m2;
cin >> a2 >> m2;//第二个方程的a2和m2
ll k1, k2; //要求的系数
ll d = exgcd(a1,a2,k1,k2); //求最大公约数同时求出了系数
if ((m2 - m1) % d) { //如果m2-m1和最大公约数不成倍数,那么无解
has_answer = 0;
break;
}
//此时求出的d为k1*a1 - k2*a2的最大公约数,而我们要求相对于m2-m1的
//则需把求出的k1,k2乘上m2-m1 / d
k1 *= (m2 - m1) / d; //更新k1
ll t = a2 / d;
k1 = (k1 % t + t) % t; //在k1的众多解中,取出最小的那个
m1 = a1 * k1 + m1; //更新m1,以进行下次合并方城
a1 = abs(a1 / d * a2); //更新a1
}
if (has_answer) {
cout << (m1 % a1 + a1) % a1 << endl; //保证负数时求出正确的模数,上面t同理(C++直接模会与数学结果不同)
}
else {
cout << -1 << endl;
}
return 0;
}