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";
}