前序
已知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;
}