※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 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;
}

 

728x90

'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

+ Recent posts