Programming/알고리즘

Lazy Propagation 완벽 이해: 세그먼트 트리에서 구간 업데이트를 효율적으로 처리하는 방법

octo54 2025. 4. 23. 14:27
반응형

Lazy Propagation 완벽 이해: 세그먼트 트리에서 구간 업데이트를 효율적으로 처리하는 방법

세그먼트 트리에서 구간 단위로 값을 업데이트해야 할 경우, 단순하게 처리하면 **시간복잡도가 O(N)**이 되어버려 효율이 급격히 떨어집니다.
이를 해결하는 고급 기법이 바로 **Lazy Propagation(지연 전파)**입니다.

이번 글에서는 Lazy Propagation의 개념, 필요성, 작동 방식, Python 코드 구현, 실전 문제 적용까지 단계별로 정리합니다.


✅ Lazy Propagation이란?

세그먼트 트리에서 여러 구간을 한 번에 갱신할 때, 갱신 정보를 지연시켜 저장해 두었다가
**필요할 때만 적용(전파)**하는 최적화 기법입니다.


📉 왜 필요한가?

기존 세그먼트 트리로 아래와 같은 연산을 수행한다고 할 때:

  • arr[2:7] += 3
  • arr[0:5] += 1
  • query(3, 4)
  • query(0, 7)

이런 구간 업데이트가 많아지면, 모든 리프 노드를 갱신하는 방식은 비효율적입니다.
→ 이때 Lazy Propagation으로 성능을 극적으로 개선할 수 있습니다.


🔍 Lazy Propagation 동작 방식 요약

  1. 업데이트 요청이 왔을 때, 현재 노드의 값은 변경하지 않음
  2. lazy 배열에 변경할 값만 저장해둠
  3. 이후 그 노드를 접근할 때(조회 또는 재귀 호출 시),
    lazy 값을 해당 노드와 자식 노드로 전파(push down)

🛠 Python 코드 예제 (구간 합 + 구간 덧셈 업데이트)

반응형
class LazySegmentTree:
    def __init__(self, arr):
        self.N = len(arr)
        self.tree = [0] * (4 * self.N)
        self.lazy = [0] * (4 * self.N)
        self.build(arr, 0, 0, self.N - 1)

    def build(self, arr, node, start, end):
        if start == end:
            self.tree[node] = arr[start]
        else:
            mid = (start + end) // 2
            self.build(arr, node*2+1, start, mid)
            self.build(arr, node*2+2, mid+1, end)
            self.tree[node] = self.tree[node*2+1] + self.tree[node*2+2]

    def push(self, node, start, end):
        if self.lazy[node] != 0:
            # 현재 노드에 lazy 반영
            self.tree[node] += (end - start + 1) * self.lazy[node]
            if start != end:
                self.lazy[node*2+1] += self.lazy[node]
                self.lazy[node*2+2] += self.lazy[node]
            self.lazy[node] = 0

    def update_range(self, l, r, val, node=0, start=0, end=None):
        if end is None:
            end = self.N - 1
        self.push(node, start, end)
        if r < start or end < l:
            return
        if l <= start and end <= r:
            self.lazy[node] += val
            self.push(node, start, end)
            return
        mid = (start + end) // 2
        self.update_range(l, r, val, node*2+1, start, mid)
        self.update_range(l, r, val, node*2+2, mid+1, end)
        self.tree[node] = self.tree[node*2+1] + self.tree[node*2+2]

    def query_range(self, l, r, node=0, start=0, end=None):
        if end is None:
            end = self.N - 1
        self.push(node, start, end)
        if r < start or end < l:
            return 0
        if l <= start and end <= r:
            return self.tree[node]
        mid = (start + end) // 2
        left = self.query_range(l, r, node*2+1, start, mid)
        right = self.query_range(l, r, node*2+2, mid+1, end)
        return left + right

✅ 사용 예제

arr = [1, 2, 3, 4, 5, 6, 7, 8]
st = LazySegmentTree(arr)

print(st.query_range(2, 5))  # 출력: 3+4+5+6 = 18

st.update_range(2, 5, 10)  # arr[2:5] += 10 → [13,14,15,6]
print(st.query_range(2, 5))  # 출력: 13+14+15+6 = 48

print(st.query_range(0, 7))  # 전체 합 = 1+2+13+14+15+6+7+8 = 66

📘 백준 Lazy Propagation 실전 문제

번호 제목 설명

10999 구간 합 구하기 2 구간 덧셈 + 구간 합
12837 가계부 구간 업데이트 & 단일 쿼리
16975 수열과 쿼리 21 구간 갱신 & 구간 쿼리

🔄 일반 구간 업데이트 방식과 비교

항목 일반 세그먼트 트리 Lazy Propagation

구간 업데이트 O(N) ✅ O(log N)
쿼리 속도 O(log N) ✅ O(log N)
구현 난이도 쉬움 약간 복잡함
실전 활용도 제한적 ✅ 고난도 문제 해결 가능

📌 결론 및 요약

  • Lazy Propagation은 세그먼트 트리의 구간 업데이트 성능을 극대화하는 핵심 기술입니다.
  • 여러 번의 업데이트와 쿼리가 반복되는 문제에서는 성능 차이가 수십 배 이상 벌어질 수 있습니다.
  • 실전에서는 반드시 이 구조를 연습하고 구현해보는 것이 중요합니다.

👉 다음 글에서는 **펜윅 트리(Fenwick Tree / Binary Indexed Tree)**를 통해
세그먼트 트리와의 차이, 사용 장단점, 구간 합 처리 방식을 비교해보겠습니다.


📚 참고자료 및 출처


 

세그먼트트리, lazy propagation, 지연전파, 구간업데이트, range update, 파이썬 자료구조, 알고리즘 최적화, 코딩테스트 고난도, 트리구조, SEO 최적화 10개