배열 기반 구현 (C++, 합)
크기 \(4N\)짜리 배열로 트리를 표현합니다. 재귀가 가장 이해하기 쉽습니다.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n;
vector<ll> seg, a;
void build(int node, int s, int e) { // [s, e]
if (s == e) { seg[node] = a[s]; return; }
int m = (s + e) / 2;
build(2 * node, s, m);
build(2 * node + 1, m + 1, e);
seg[node] = seg[2 * node] + seg[2 * node + 1]; // 결합
}
void update(int node, int s, int e, int idx, ll val) {
if (s == e) { seg[node] = val; return; }
int m = (s + e) / 2;
if (idx <= m) update(2 * node, s, m, idx, val);
else update(2 * node + 1, m + 1, e, idx, val);
seg[node] = seg[2 * node] + seg[2 * node + 1];
}
ll query(int node, int s, int e, int l, int r) { // [l, r] 합
if (r < s || e < l) return 0; // 전혀 안 겹침 → 항등원
if (l <= s && e <= r) return seg[node]; // 완전히 포함
int m = (s + e) / 2;
return query(2 * node, s, m, l, r)
+ query(2 * node + 1, m + 1, e, l, r);
}
int main() {
cin >> n;
a.assign(n, 0); seg.assign(4 * n, 0);
for (auto& x : a) cin >> x;
build(1, 0, n - 1);
// update(1, 0, n-1, idx, val);
// query(1, 0, n-1, l, r);
}
각 연산의 호출은 build(1, 0, n-1)처럼 루트(1번)에서 시작합니다.
최솟값 트리로 바꾸기
연산을 합에서 최솟값으로 바꾸려면 결합 연산 과 항등원 만 바꿉니다.
| 연산 |
결합 |
항등원(안 겹침 반환값) |
| 합 |
a + b |
0 |
| 최솟값 |
min(a, b) |
INF |
| 최댓값 |
max(a, b) |
-INF |
| GCD |
gcd(a, b) |
0 |
seg[node] = ... 부분과 안 겹침 시 반환값만 교체하면 됩니다.
파이썬 (반복형, 합)
재귀가 느린 파이썬은 흔히 반복형(iterative) 세그를 씁니다.
class SegTree:
def __init__(self, data):
self.n = len(data)
self.t = [0] * (2 * self.n)
self.t[self.n:] = data # 리프
for i in range(self.n - 1, 0, -1): # 내부 노드
self.t[i] = self.t[2 * i] + self.t[2 * i + 1]
def update(self, i, val): # 0-based
i += self.n
self.t[i] = val
i >>= 1
while i:
self.t[i] = self.t[2 * i] + self.t[2 * i + 1]
i >>= 1
def query(self, l, r): # [l, r)
res = 0
l += self.n; r += self.n
while l < r:
if l & 1: res += self.t[l]; l += 1
if r & 1: r -= 1; res += self.t[r]
l >>= 1; r >>= 1
return res
반복형은 [l, r) 반열림 구간을 쓴다는 점에 주의하세요.
흔한 함정
- 배열 크기 — 재귀형은
4 * n을 확보해야 안전합니다. \(2N\)만 잡으면
넘칠 수 있습니다.
- 항등원 — 안 겹침일 때 반환값이 연산의 항등원이어야 합니다(합 0, min은 INF).
- 0/1-based, 구간 표기 — 재귀형은 보통
[s, e] 닫힘, 반복형은 [l, r)
반열림. 섞으면 off-by-one.
- 갱신 후 부모 재계산 누락 — 리프만 바꾸고 위로 안 올리면 질의가 틀립니다.
- 자료형 — 합은 쉽게 커지니
long long.
응용 패턴
- 구간 합/최값/GCD + 점 갱신 — 기본형.
- 구간 갱신 + 구간 질의 — 느리게 갱신되는 세그(lazy)로 확장(상위 단원).
- 카운팅/순위 — 값을 인덱스로 둔 세그로 "X 이하 개수", k번째 수.
- 2차원/머지 소트 트리 — 노드에 추가 자료구조를 얹는 변형.
"구간을 \(O(\log N)\)개 노드로 분해한다"는 한 가지 발상이 점 갱신과 구간 질의를
동시에 빠르게 만든다는 점이 핵심입니다. 결합 연산만 바꾸면 폭넓게 재사용됩니다.