풀 수 없어 보이는 구간 갱신
다음 연산을 모두 \(O(\log)\) 근처로 처리하고 싶다.
- 구간 \([l, r]\)의 모든 원소에 \(a_i \leftarrow \min(a_i, x)\) (chmin)
- 구간 합 / 구간 최댓값 질의
chmin은 일부 원소만 바꾸므로 평범한 lazy로는 안 된다. Segment Tree Beats(지대룡 트릭) 의 핵심은 조건부 lazy다.
노드가 저장하는 정보
각 노드에 다음을 유지한다.
| 변수 | 의미 |
|---|---|
mx1 |
구간 최댓값 |
mx2 |
구간 두 번째 최댓값(strict, < mx1) |
cnt |
최댓값을 가진 원소 개수 |
sum |
구간 합 |
chmin의 세 갈래
구간에 \(\min(\cdot, x)\)를 적용할 때 노드에서:
- \(x \ge mx1\): 아무것도 안 바뀜 → 즉시 종료(break).
- \(mx2 < x < mx1\): 최댓값 원소들만 \(x\)로 줄어듦.
-sum -= (mx1 - x) * cnt,mx1 = x. → lazy로 처리(tag). - \(x \le mx2\): 경계가 애매함 → 자식으로 재귀(recurse).
세 번째 경우만 깊이 들어간다. 이 "비싼" 경우가 드물게 일어난다는 것이 복잡도의 핵심이다.
복잡도: amortized \(O(n \log^2 n)\)
증명은 potential 함수 \(\Phi\) = (모든 노드에서 서로 다른 최댓값 태그의 수)로 한다. case 3 재귀가 일어날 때마다 서로 다른 값의 "종류"가 합쳐져 \(\Phi\)가 감소한다. 각 chmin 연산이 \(\Phi\)를 \(O(\log n)\)만큼 증가시키고, 비싼 재귀가 \(\Phi\)를 그만큼 소모하므로 총 작업량이 \(O((n+q)\log^2 n)\)로 묶인다.
엄밀히: \(\Phi = \sum_v (\text{노드 } v \text{ 서브트리에 나타나는 서로 다른 값의 개수})\). 초기 \(\Phi = O(n \log n)\). 각 case-3 하강은 \(\Phi\)를 최소 1 감소시킨다.
확장: 양방향 + 구간 덧셈
chmax를 추가하려면 최솟값 쌍(mn1, mn2, cntmin)도 같이 유지한다. 구간 덧셈(add)까지 넣으면 lazy가 (add 태그, chmin 태그, chmax 태그) 세 종류로 늘고, 태그 합성 순서를 조심해야 한다. 일반적으로 add → chmin/chmax 순으로 정규화한다.
정리
- chmin/chmax는 "최댓값/최솟값만 건드리는 lazy"로 본다.
- break / tag / recurse의 3분기가 핵심.
- 분석은 potential argument로 amortized \(O(\log^2)\).
다음 강의에서 add + chmin + 구간합을 모두 지원하는 구현을 본다.