Segment Tree (세그먼트 트리)
RMQ(Range Minimum/Maximum Query) : 구간 최소/최대 쿼리 문제를 해결할 때 사용
코드
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef struct Node {
int a, b, idx;
} Node;
int n, tre[103 << 2];
Node a[103];
bool compare(Node a, Node b)
{
return a.a < b.a;
}
bool compare2(Node a, Node b)
{
return a.b < b.b;
}
int query(int l, int r, int node, int start, int end)
{
if (r < start || l > end)
return 0;
if (start <= l && r <= end)
return tre[node];
int mid = (l + r) / 2;
return max(query(l, mid, node * 2, start, end), query(mid + 1, r, node * 2 + 1, start, end));
}
int update(int l, int r, int node, int idx, int val)
{
if (r < idx || l > idx)
return tre[node];
if (l == r)
return tre[node] = val;
int mid = (l + r) / 2;
return tre[node] = max(update(l, mid, node * 2, idx, val), update(mid + 1, r, node * 2 + 1, idx, val));
}
int main()
{
int i, j, k;
scanf("%d", &n);
for (i = 1; i <= n; i++) {
scanf("%d%d", &j, &k);
a[i].a = j;
a[i].b = k;
}
sort(a + 1, a + 1 + n, compare);
for (i = 1; i <= n; i++)
a[i].idx = i;
sort(a + 1, a + 1 + n, compare2);
for (i = 1; i <= n; i++) {
if (a[i].idx == 5)
i = i;
int cnt = query(1, n, 1, 1, a[i].idx);
update(1, n, 1, a[i].idx, cnt + 1);
}
printf("%d", n - query(1, n, 1, 1, n));
return 0;
}
전깃줄(https://www.acmicpc.net/problem/2565) - 구간합 사례 (n 보다 작은 수가 몇개 있었는가 -> LIS)
#include <stdio.h>
#include <queue>
#define MAX 65535
using namespace std;
typedef long long int lld;
int tre[70000 << 2];
int update(int l, int r, int node, int pos, int diff) {
if (pos < l || r < pos)
return tre[node];
if (l == r)
return tre[node] += diff;
int mid = (l + r) / 2;
return tre[node] = update(l, mid, node * 2, pos, diff) + update(mid + 1, r, node * 2 + 1, pos, diff);
}
int kth(int l, int r, int k, int node) {
if (l == r)
return l;
int mid = (l + r) / 2;
if (tre[node * 2] >= k)
return kth(l, mid, k, node * 2);
else
return kth(mid + 1, r, k - tre[node * 2], node * 2 + 1);
}
int main()
{
int n, k, i, j;
queue<int> q;
lld result = 0;
scanf("%d%d", &n, &k);
for (i = 1; i <= n; i++) {
scanf("%d", &j);
q.push(j);
update(0, MAX, 1, j, 1);
if (q.size() == k) {
result += kth(0, MAX, (k + 1) / 2, 1);
update(0, MAX, 1, q.front(), -1);
q.pop();
}
}
printf("%lld", result);
return 0;
}
중앙값 측정 (https://www.acmicpc.net/problem/9426 - 구간합 + kth 구현 사례
정리
- 포화이진트리로 표현, n << 2 만큼의 메모리 필요
- 초기화(build) : O(N)
- 질의(query) : O(logN)
- 트리 탐색 구간과 조회하려는 위치의 교집합에 따라 다음의 처리를 한다
- 공집합 : 생각하지 않는다
- 탐색구간 : 그대로 반환
- 이외의 경우 : 자식들 중에서 최적의 답을 반환
- 이외에도 트리의 성질을 활용해서 1...node 까지의 합이 k 가 되는 지점을 검색할 수 있다
- 순열 문제(1849)
- 트리 탐색 구간과 조회하려는 위치의 교집합에 따라 다음의 처리를 한다
- 업데이트(update) : O(logN)
- 왼쪽 자식이 업데이트 노드를 포함 : 왼쪽 업데이트 호출
- 오른쪽 자식이 업데이트 노드를 포함 : 오른쪽 업데이트 호출
- 자식들을 통해 현재 노드에 대한 값을 업데이트
- Query 의 종류 : max, min, sum, xor, nth(몇 번째), list(merge sort tree)
- 범위 검색 및 업데이트를 할 경우 lazy propagation 을 사용한다, O((logN)^2)
- 직사각형 면적의 합을 구하는 문제는 plane sweeping + segment tree 를 이용하여 풀 수 있다
관련 문제
- 최소값 (https://www.acmicpc.net/problem/10868)
- 최소값과 최대값 (https://www.acmicpc.net/problem/2357)
- 구간 합 구하기 (https://www.acmicpc.net/problem/2042)
- 구간 합 구하기 2 (https://www.acmicpc.net/problem/10999)
- 구간 합 구하기 3: https://www.acmicpc.net/problem/11658
- 구간 곱 구하기 11505
- 가계부(Hard) 12837
- 가장 긴 증가하는 부분 수열 2 : 12015
- 커피숍2 : 1275
- 수들의 합 : 2268
- 오름세 : 3745
- 꼬인 전깃줄 : 1365
- 터보소트 : 3006
- 나무심기 1280 ★
- 디지털 비디오 디스크 : 9345 ★
- 사탕상자: https://www.acmicpc.net/problem/2243 ★
- 공장: https://www.acmicpc.net/problem/7578
- 순열: https://www.acmicpc.net/problem/1849
- 굉장한 학생: https://www.acmicpc.net/problem/2336 ★
- 영화 수집: https://www.acmicpc.net/problem/3653 ★
- 세그먼트 트리 응용 문제 : https://www.acmicpc.net/workbook/view/822