December 21, 2020
세그먼트 트리
세그먼트 트리는 구간에 대한 정보를 저장하는데 쓰이는 트리 자료구조이다. 저장된 구간에서 주어진 구간을 빠르게 쿼리할 수 있어 쿼리 문제에 자주 등장한다. 특히 특정 구간의 최솟값 또는 최댓값을 찾는 문제는 자주 출제되는 문제이다. 이런 문제를 구간 최소 쿼리(range minimum query, RMQ)라 부른다.
문제에 나온 예제를 위의 그림처럼 19개의 구간으로 나눌 수 있다. 이는 이진 트리 구조이지만 메모리 절약을 위해 1차원 배열로 표현한다. 초기화 과정은 아래와 같다.
query 메서드에서 nodeLeft
와 nodeRight
는 해당 노드의 표현 범위를 나타낸다.
node
가 3이면 각각 5와 9를 가리킨다.#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int INF = 1e9;
struct RMQ {
int n;
vector<int> rangeMin;
RMQ(const vector<int>& array) {
n = array.size();
rangeMin.resize(n * 4);
init(array, 1, 0, n - 1);
}
int init(const vector<int>& array, int node, int left, int right) {
if (left == right) return rangeMin[node] = array[left];
int mid = (left + right) / 2;
return rangeMin[node] = min(init(array, node * 2, left, mid),
init(array, node * 2 + 1, mid + 1, right));
}
int query(int left, int right, int node, int nodeLeft, int nodeRight) {
if (right < nodeLeft || nodeRight < left) return INF;
if (left <= nodeLeft && nodeRight <= right) return rangeMin[node];
int mid = (nodeLeft + nodeRight) / 2;
return min(query(left, right, node * 2, nodeLeft, mid),
query(left, right, node * 2 + 1, mid + 1, nodeRight));
}
int query(int left, int right) { return query(left, right, 1, 0, n - 1); }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
vector<int> arr(n), arr2(n);
for (int i = 0; i < n; i++) {
cin >> arr[i];
arr2[i] = -arr[i];
}
RMQ rangeMinQuery(arr);
RMQ rangeMaxQuery(arr2);
for (int i = 0, a, b; i < m; i++) {
cin >> a >> b;
cout << rangeMinQuery.query(a - 1, b - 1) << ' ';
cout << -rangeMaxQuery.query(a - 1, b - 1) << '\n';
}
return 0;
}