// 研究用ファイル #include using namespace std; using ll = long long; using vi = vector; using pii = pair; using vl = vector; using pll = pair; ll my_gcd(ll a, ll b) { a = abs(a); b = abs(b); if (a < b) swap(a, b); if (a == 0) return 0; // (0,0) // recursive if (b == 0) return a; // ll r = a % b; return my_gcd(b, a % b); // not recursive // while (b > 0) { // ll r = a % b; // ll q = a / b; // a = b; // b = r; // } // return a; } ll my_gcd(vl a) { ll g = 0; for (ll a_i : a) g = my_gcd(g, a_i); return g; } pll ext_gcd(ll _a, ll _b) { vl a = {abs(_a), abs(_b)}; if (a[0] < a[1]) swap(a[0], a[1]); if (a[0] == 0) return pll(0, 0); // (0,0) else if (a[1] == 0) { // (a,0) ll x = 1, y = 0; if (abs(_a) < abs(_b)) swap(x, y); if (_a < 0) x = -x; if (_b < 0) y = -y; return pll(x, y); } int n = 2; // number of terms while (a[n - 1] > 0) { a.push_back(a[n - 2] % a[n - 1]); ++n; } // for (ll r : a) cout << r << ", "; cout << endl; ll x = 0, y = 1; // printf("(x, y) = (%lld, %lld)\n", x, y); for (int i = n - 2; i >= 2; --i) { ll q = a[i - 2] / a[i - 1]; ll temp_x = y; ll temp_y = x - q * y; x = temp_x; y = temp_y; // cout << q << endl; printf("(x, y) = (%lld, %lld)\n", x, y); } if (abs(_a) < abs(_b)) swap(x, y); if (_a < 0) x = -x; if (_b < 0) y = -y; // assert(_a * x + _b * y == my_gcd(_a, _b)); // return pll(x, y); } vl ext_gcd(vl a) { int n = a.size(); vl x(n); ll g; if (n == 0) { return x; } else if (n == 1) { if (a[0] > 0) x[0] = 1; else if (a[0] < 0) x[0] = -1; return x; } else if (n >= 2) { tie(x[0], x[1]) = ext_gcd(a[0], a[1]); g = my_gcd(a[0], a[1]); } // cout << "g: " << g << endl; // cout << "x: "; for (int i = 0; i < n; ++i) cout << x[i] << ", "; cout << endl; for (int i = 2; i < n; ++i) { ll X, Y; tie(X, Y) = ext_gcd(g, a[i]); for (int j = 0; j < i; ++j) { x[j] *= X; } x[i] = Y; g = my_gcd(g, a[i]); // printf("(X, Y) = (%lld, %lld)\n", X, Y); // cout << "g: " << g << endl; // cout << "x: "; for (int i = 0; i < n; ++i) cout << x[i] << ", "; cout << endl; } return x; } // mod m での a の逆元の拡張: a * x = g = gcd(a, m) なる0 <= x < m // a*x ≡ g mod m // a*x + m*y ≡ g ll my_inv(ll a, ll m) { assert(m >= 1); ll x = ext_gcd(a, m).first; if (x < 0) x += m; return x; } // 合同方程式 x ≡ r[i] mod m[i] の解を x ≡ y mod z として、(y,z)を返す // m[i] >= 1 // n = 2 // g = gcd(m1,m2) = 1 のとき、 // m1 * x1 + m2 * x2 = 1 となるx1,x2を見つけ、 // x = r1*m2*x2 + r2*m1*x1 とすればよい // g > 1 のとき、 // r1 ≡ r2 (≡ r0) mod g を前提として、 // s1 = (r1 - r0) / g, s2 = (r2 - r0) / g とする pll my_crt(ll r1, ll r2, ll m1, ll m2) { assert(m1 >= 1); assert(m2 >= 1); ll g = my_gcd(m1, m2), l = m1 / g * m2; ll r = r1 % g; if ((r2 - r) % g != 0) return pll(0, 0); ll s1 = (r1 - r) / g, s2 = (r2 - r) / g; ll n1 = m1 / g, n2 = m2 / g; ll y1, y2; tie (y1, y2) = ext_gcd(n1, n2); ll y = (s1 * n2 * y2 + s2 * n1 * y1) % (l / g); // if (y < 0) y += l / g; ll x = (y * g + r) % l; if (x < 0) x += l; return pll(x, l); } pll my_crt(vl r, vl m) { int n = r.size(); assert(m.size() == n); ll y, z; if (n == 0) { return pll(0, 1); } else if (n == 1) { assert(m[0] >= 1); ll y = r[0] % m[0], z = m[0]; if (y < 0) y += z; return pll(y, z); } else if (n >= 2) { tie(y, z) = my_crt(r[0], r[1], m[0], m[1]); } for (int i = 2; i < n && z > 0; ++i) { tie(y, z) = my_crt(y, r[i], z, m[i]); } return pll(y, z); } // test void test_my_gcd() { // ext_gcd ll a, b; a = rand(); if (rand() % 2) a = -a; b = rand(); if (rand() % 2) b = -b; cin >> a >> b; printf("(a, b) = (%lld, %lld)\n", a, b); printf("%lld %lld %lld\n", a, b, my_gcd(a, b)); ll x, y; tie(x, y) = ext_gcd(a, b); printf("gcd(%lld, %lld) = %lld, (x, y) = (%lld, %lld)\n", a, b, my_gcd(a, b), x, y); } void test_my_gcd_multi() { vl a; int n; // n = rand() % 10; cin >> n; cout << n << endl; for (int i = 0; i < n; ++i) { // a.push_back(rand()); int a_i; cin >> a_i; a.push_back(a_i); } for (int a_i : a) cout << a_i << ", "; cout << endl; cout << my_gcd(a) << endl; } void test_ext_gcd_multi() { int n; cin >> n; vl a(n); for (int i = 0; i < n; ++i) { ll a_i; cin >> a_i; a[i] = a_i; } vl x = ext_gcd(a); for (int x_i : x) cout << x_i << " "; cout << endl; } void test_my_inv() { ll a, m; cin >> a >> m; printf("%lld * %lld = %lld (mod %lld)\n", a, my_inv(a, m), my_gcd(a, m), m); } void test_my_crt() { int n; // cin >> n; n = 3; vl r(n), m(n); for (int i = 0; i < n; ++i) { cin >> r[i] >> m[i]; } ll y, z; tie(y, z) = my_crt(r, m); if (z == 0) cout << -1 << endl; else cout << y << endl; // cout << y << " " << z << endl; // cout << y << endl; } int main() { srand((unsigned)time(NULL)); // test_my_gcd(); // test_my_gcd_multi(); // test_ext_gcd_multi(); // test_my_inv(); test_my_crt(); }