笔记---中国剩余定理

全程学自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],xmi ( 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 1ai ≤ 231 − 1 , 0 ≤ m ≤231−1,0≤m 2311,0mi < a <ai
1 ≤ n ≤ 25 1≤n≤25 1n25
所有 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) ax1(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),需要在每一步去合并方程
过程如下:
笔记---中国剩余定理_第1张图片
代码如下:

#include
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;
}

你可能感兴趣的:(笔记,算法,c++)