※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※
이번에 볼 문제는 백준 32383번 문제인 점프이다.
문제는 아래 링크를 확인하자.
https://www.acmicpc.net/problem/32383
가장 낮은 건물 에서 다른 건물로 이동할 때 다음 이동이 어떻게 되어야 하는지 생각해보자. 모든 건물의 높이는 서로 다르다는 조건에 의해 이는 항상 존재한다.
이 건물이 맨 왼쪽 또는 오른쪽에 있다면 다음 이동은 항상 유일하게 인접한 건물이 될 것이다. 그보다 더 높은 높이의 건물이 이후에 나오더라도 해당 건물을 경유해 이동하는 것이 항상 체력상 이득이기 때문이다.
이 건물이 맨 왼쪽 또는 오른쪽이 아니라면 왼쪽 건물과 오른쪽 건물이 모두 존재할 것이다. 이 중 더 높은 건물로 향하는 경우 한번에 그 건물로 이동하는 것보다 더 낮은 건물을 경유해서 이동하는 것이 항상 이득임을 알 수 있다. 또한 그 외의 건물로의 이동은 앞서 본 것과 같은 이유로 항상 손해임을 알 수 있다.
한편, 어떤 두 건물 사이의 직접 이동이 가능한 경우 이동 과정에서 굳이 두 건물 모두보다 낮은 건물로 이동했다가 다시 이동하는 작업은 필요없음을 관찰하자. 따라서 위에서 구한 이동을 제외하면 가장 낮은 건물을 경유하는 이동은 더 존재하지 않음을 알 수 있다.
이러한 관찰을 적절히 일반화하면, 모든 이동은 (가장 높은 건물을 제외한) 각 건물의 가장 가까운 왼쪽 및 오른쪽 높은 건물(단, 해당 방향에 존재하는 경우에 한정한다.)로의 이동의 형태만으로 이루어져야 함을 알 수 있다. 이러한 이동을 모으면 트리가 되고, 이 트리 위에서의 모든 경로(가중치는 각 이동의 체력 소모량과 같고, 방향만 다른 것은 같은 경로로 친다)의 가중치의 합을 계산하는 것으로 문제를 해결할 수 있게 된다.
이 문제의 해결 과정에서 구성한 것과 같은 트리를 Cartesian Tree(데카르트 트리)라 부른다. Cartesian Tree는 선형 시간에도 구성할 수 있음이 잘 알려져 있으니 관심이 있다면 찾아보자.
아래는 제출한 소스코드이다.
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
int N;
int A[500002][2];
int L[500002], R[500002];
int findL(int x) {
if (x == L[x]) return x;
return L[x] = findL(L[x]);
}
int findR(int x) {
if (x == R[x]) return x;
return R[x] = findR(R[x]);
}
ll ans;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> N;
for (int i = 1; i <= N; i++) L[i] = i, R[i] = i;
for (int i = 1; i <= N; i++) {
cin >> A[i][0], A[i][1] = 1;
pq.push(make_pair(A[i][0], i));
}
while (!pq.empty()) {
int cur = pq.top().second; pq.pop();
L[cur] = cur - 1, R[cur] = cur + 1;
int l = findL(cur), r = findR(cur);
if (!l && !r) continue;
if (!l) {
ll g = A[r][0] - A[cur][0];
ans += g * g % 1000000007 * A[cur][1] % 1000000007 * (N - A[cur][1]) % 1000000007;
A[r][1] += A[cur][1];
}
else if (!r) {
ll g = A[l][0] - A[cur][0];
ans += g * g % 1000000007 * A[cur][1] % 1000000007 * (N - A[cur][1]) % 1000000007;
A[l][1] += A[cur][1];
}
else if (A[l][0] < A[r][0]) {
ll g = A[l][0] - A[cur][0];
ans += g * g % 1000000007 * A[cur][1] % 1000000007 * (N - A[cur][1]) % 1000000007;
A[l][1] += A[cur][1];
}
else {
ll g = A[r][0] - A[cur][0];
ans += g * g % 1000000007 * A[cur][1] % 1000000007 * (N - A[cur][1]) % 1000000007;
A[r][1] += A[cur][1];
}
}
cout << ans % 1000000007;
}
'BOJ' 카테고리의 다른 글
[BOJ 2014 // C++] 소수의 곱 (1) | 2024.10.25 |
---|---|
[BOJ 16021 // C++] Choose your own path (2) | 2024.10.24 |
[BOJ 6248 // C++] Bronze Cow Party (1) | 2024.10.22 |
[BOJ 5526 // C++] ダーツ (Darts) (2) | 2024.10.21 |
[BOJ 1229 // C++] 육각수 (1) | 2024.10.18 |