全程学自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 longll 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和m1for (int i = 0; i < n - 1; i++) { //要合并n-1次方程ll a2, m2;cin >> a2 >> m2;//第二个方程的a2和m2ll 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 / dk1 *= (m2 - m1) / d; //更新k1ll 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;
}