BOJ15732

도토리

Posted by Damin on September 18, 2019

15732 도토리 숨기기

문제 링크

처음에 이 문제를 보고, 이거를 어떻게 풀어야 할지 몰랐다.
도토리의 개수가 10^9까지 있을 수 있다.
상자를 규칙마다 다 만들어 주기에는 시간 초과다.
만들어 준다 한들, 도토리의 개수가 10^9개 까지 있어서 또 시간 초과다.
그럼 이 문제를 어떻게 할까… 하다가!!
상자 개수를 이분 탐색으로 풀면 어떨까 했다!!
또, 각 규칙마다 ((끝나는 상자(B) - 시작 상자(A)) / 간격) 을 해주면,
그 상자의 들어가는 도토리의 개수가 나온다.

예를 들어, test case를 보자
상자의 개수를 130으로 두면
첫 번째 규칙에서 100,110,120,130 = 4개
두 번째 규칙에서 110,125 = 2개

이런 식으로 나눗셈을 하면 O(1)시간 * 규칙의 개수 O(k) * O(log N) 의 시간이 걸린다.
따라서 O(10^4log 1,000,000) = O(610^4) 시간이 걸린다

이런 식으로 도토리의 개수를 다 더해주면서 D보다 크거나 같은 것을 봐준다.
크거나 같다는 뜻은, 마지막 도토리가 들어가거나, 더 많은 도토리가 들어간다는 뜻이다.
따라서 크거나 같은 상자에서 min값을 바꿔주면 마지막 상자라는 뜻이 된다.

이상으로 풀이 마치겠습니다. 이 문제 좋은 문제인거 같아서 추천드립니다!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<pair<ll, ll>, ll>p;
vector<p>v;
int main() {
	ll n, k, d, a, b, c, s = 1e9, e = 0, ans = 1e9;
	cin >> n >> k >> d;
	v.reserve(n + 1);
	for (int i = 0; i < k; i++) {
		cin >> a >> b >> c;
		v.push_back({ {a,b},c });
		s = min(s, a);
		e = max(e, b);
	}
	while (s <= e) {
		ll mid = (s + e) / 2;
		ll answer = 0;
		for (int i = 0; i < k; i++) {
			if (v[i].first.first <= mid && v[i].first.second >= mid) {
				ll damin = mid - v[i].first.first;
				ll divi = v[i].second;
				if (damin == 0) answer++;
				else {
					answer += (damin / divi) + 1;
				}
			}
			else if (mid > v[i].first.second) {
				answer += ((v[i].first.second - v[i].first.first) / v[i].second + 1);
			}
		}
		if (answer < d) {
			s = mid + 1;
		}
		else {
			e = mid - 1;
			ans = min(ans, mid);
		}
	}
	cout << ans << "\n";
}