카테고리 없음

백준 34036 - 중국인의 나머지 정리

rimugiri 2025. 7. 5. 18:03
728x90

 

중국인의 나머지 정리와 확장 유클리드의 개념을 이해하고 적용해보기 딱 좋은 문제입니다. ( 솔직히 이 문제는 이를 사용할 필요는 없습니다)

 

1. 중국인의 나머지 정리란?

 

중국인의 나머지 정리(Chinese Remainder Theorem, CRT)는 여러 개의 합동식을 동시에 만족시키는 정수 해를 찾는 방법입니다.

간단히 말해, 다음과 같은 문제를 푸는 데 사용됩니다.

"어떤 수 x를 3으로 나누면 나머지가 2이고, 5로 나누면 나머지가 3이다. 이 두 조건을 모두 만족하는 x는 무엇일까?"

 

이는 모두가 만나는 지점 가 P ≡ Xᵢ (mod Sᵢ)라는 규칙을 만족해야 함을 의미합니다. 즉, 이 문제는 개의 합동식을 동시에 만족시키는 가장 작은 를 찾는 전형적인 중국인의 나머지 정리 문제입니다.

다만, 보폭 들이 항상 서로소가 아니므로, 두 개의 합동식을 하나로 합쳐나가는 방식의 확장된 중국인의 나머지 정리가 필요합니다.

2. 확장 유클리드 호제법이란?

확장 유클리드 호제법(Extended Euclidean Algorithm)은 두 정수 a와 b의 최대공약수(gcd(a, b))를 구하는 동시에, ax + by = gcd(a, b) 라는 등식을 만족하는 정수 해 x와 y를 찾아내는 알고리즘입니다.

 

이것이 왜 필요할까요?

중국인의 나머지 정리를 푸는 과정에서 두 합동식 P ≡ a₁ (mod n₁)과 P ≡ a₂ (mod n₂)를 합치려면, k * n₁ ≡ a₂ - a₁ (mod n₂) 형태의 선형 합동 방정식을 풀어야 합니다. 확장 유클리드 호제법은 바로 이 방정식을 푸는 데 필요한 k 값을 계산하는 핵심적인 역할을 합니다. 

 

3. 풀이코드

#include <bits/stdc++.h>

#define FAST ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
typedef long long ll;

using namespace std;

ll gcd(ll a, ll b) {
	while (b) {
		a %= b;
		swap(a, b);
	}

	return a;
}

ll extendedGCD(ll a, ll b, ll& x, ll& y) {
	if (b == 0) {
		x = 1;
		y = 0;
		return a;
	}

	ll x1, y1;
	ll g = extendedGCD(b, a % b, x1, y1);
	x = y1;
	y = x1 - (a / b) * y1;
	return g;
}

struct Point {
	ll curPos, moveDist;
	Point(ll pos, ll dist) : curPos(pos), moveDist(dist) {}
	Point() : curPos(0), moveDist(0) {}
};

int main()
{
	FAST;

	int n; cin >> n;
	vector<Point> arr(n);

	// 최소로 넘어야 하는 위치
	ll maxPos = 0;

	for (int i = 0; i < n; i++) {
		cin >> arr[i].curPos >> arr[i].moveDist;
		maxPos = max(maxPos, arr[i].curPos);
	}
	
	ll cur = arr[0].curPos;
	ll curDist = arr[0].moveDist;

	for (int i = 1; i < n; i++) {
		ll next = arr[i].curPos;
		ll nextDist = arr[i].moveDist;

		ll x, y;
		ll g = extendedGCD(curDist, nextDist, x, y);

		// 해가 없을 조건
		if ((next - cur) % g != 0) {
			cout << -1;
			return 0;
		}

		ll k = x * ((next - cur) / g);
		ll m = nextDist;
		k = (k % m + m) % m; // k를 양의 해로 바꿔준다

		ll newCur = cur + k * curDist;
		ll newDist = (curDist / g) * nextDist; // lcm
		
		cur = newCur % newDist;
		curDist = newDist;
	}

	// 답은 최소 위치를 넘어야 된다.
	if (cur < maxPos) {
		ll offset = maxPos - cur;
		ll steps = (offset + curDist - 1) / curDist;
		cur += steps * curDist;
	}

	cout << cur;


	return 0;
}
728x90