中国剩余定理(个人笔记)

前序

已知a和x0,且x % a ≡ x0, 则x = (x0 % a + a) % a


1. 扩展欧几里德算法

求出a与b的最大公因数d的同时,还能解出ax+by=gcd(a,b)中(x,y)的一组特解

在这里插入图片描述

int exgcd(int a, int b, int &x, int &y){
    if(b == 0) {x = 1, y = 0; return a; }
    int d = exgcd(b, a % b, x, y);
    int tmp = x;
    x = y, y = tmp - a / b * y;
    return d;
}

这里如果将x与y交换去调用exgcd函数,就可以得到以下代码

int exgcd(int a, int b, int &x, int &y){
    if(b == 0) { x = 1, y = 0; return a; }
    int d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

解得的x和y为它们的一组特解(x0, y0),它们的通解为x = x0 + b / gcd(a,b), y = y0 - a / gcd(a,b)


2.同余方程

给出a,b和m,求满足a ∗ x ≡ b (mod m)的x,且x为最小正整数解

变形 ==> a * x = k * m + b, 即 a * x - k * m = b

只要满足b % gcd(a, m) == 0, 那么就一定存在解x

直接带入exgcd函数即可求出一组特解(x0, k0)满足a * x - k * m = gcd(a, m),所以x1 = x0 * b / gcd(a, m)的值就满足a * x - k * m = b

为了得到最小正整数解,只需令x2 = (x1 % m + m) % m,那么x2就是最终的结果


3.中国剩余定理

给出n组式子x ≡ mi (mod ai)中的mi,ai,求出一个最小正整数x满足这n组式子

取其中两个式子,可以得到

x = k1 * a1 + m1
x = k2 * a2 + m2

那么整理得到

a1 * k1 - a2 * k2 = m2 - m1

(此时需要判断是否存在解!!!也就是说,(m2 - m1) % gcd(a1, a2) == 0必须成立才能有解

这样,通过调用exgcd函数就可以得到一个特解k1 *= (m2 - m1) / gcd(a1, a2),通解为k1 + k * a2 / gcd(a1, a2), 同理k2的通解为 k2 + k * a1 / gcd(a1,a2)(因为上边的式子为减号,所以这里的两个通解全为加k倍,而不是一加一减),那么

t = a2 / gcd(a1, a2)
x = [k1 + k * t] * a1 + m1
  = a1 * t * k + a1 * k1 + m1

为了让x变得更小,这里我们可以让k1 + k * t = (k1 % t + t) % t

此时我们可以将a1 * t当作新的a将a1 * k1 + m1当作新的m,即m1 = a1 * k1 + m1, a1 = a1 * t,也满足x = k1 * a1 + m1的格式,就将x = k1 * a1 + m1 和 x = k2 * a2 + m2合并成了一个式子

同理,最后可以将n个式子合并成一个式子,只剩下更改后的a1和m1,那么答案x = (m1 % a1 + a1) % a1

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

ll exgcd(ll a, ll b, ll& x, ll& y) {
	if (b == 0) { x = 1, y = 0; return a; }
	ll d = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}

int main(void)
{
	bool flag = true;
	int n; cin >> n;
	ll a1, m1; cin >> a1 >> m1;
	for (int i = 1; i < n; ++i) {
		ll a2, m2; cin >> a2 >> m2;

		ll k1, k2;
		ll d = exgcd(a1, -a2, k1, k2);

		if ((m2 - m1) % d != 0) { flag = false; break; }

		k1 *= (m2 - m1) / d;
		ll t = a2 / d;
		k1 = (k1 % t + t) % t;

		m1 = a1 * k1 + m1;
		a1 = abs(a1 * t);
	}
	if (flag) cout << (m1 % a1 + a1) % a1 << endl;
	else cout << "-1\n";
	return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值