Programming/알고리즘

Link/Cut Tree에서 경로 쿼리 처리: 합, 최댓값, 노드 갱신까지 완전 이해

octo54 2025. 6. 2. 11:01
반응형

Link/Cut Tree에서 경로 쿼리 처리: 합, 최댓값, 노드 갱신까지 완전 이해

이번 글에서는 **Link/Cut Tree(LCT)**에서
경로상의 합, 최댓값, 노드 값 변경 등의 쿼리 처리를 어떻게 구현하는지
세부적으로 설명합니다.

앞선 글에서 LCT의 구조와 기본 연산들을 익혔다면,
이제는 실전 문제에서 많이 등장하는 경로 쿼리(Path Query) 기능을
어떻게 Augmentation으로 구현하는지를 마스터할 차례입니다.


✅ 목표 연산 정리

연산 설명

path_sum(u, v) u–v 경로의 값 합계
path_max(u, v) u–v 경로의 최댓값
update_node(u, val) 노드 u의 값을 변경
update_path(u, v, val) u–v 경로 값 일괄 변경 (Lazy Propagation 포함)

🔧 기본 LCT 구조 복습

class LCTNode:
    def __init__(self, value):
        self.value = value
        self.path_sum = value
        self.path_max = value
        self.left = None
        self.right = None
        self.parent = None
        self.rev = False
        self.lazy_add = 0  # for range add

🧠 Augmentation 핵심 로직

반응형

1. update(node): 자식 정보 갱신

def update(node):
    node.path_sum = node.value
    node.path_max = node.value
    if node.left:
        node.path_sum += node.left.path_sum
        node.path_max = max(node.path_max, node.left.path_max)
    if node.right:
        node.path_sum += node.right.path_sum
        node.path_max = max(node.path_max, node.right.path_max)

2. push(node): Lazy 및 rev 처리

def push(node):
    if node.rev:
        node.left, node.right = node.right, node.left
        if node.left:
            node.left.rev ^= True
        if node.right:
            node.right.rev ^= True
        node.rev = False

    if node.lazy_add != 0:
        if node.left:
            apply_lazy(node.left, node.lazy_add)
        if node.right:
            apply_lazy(node.right, node.lazy_add)
        node.lazy_add = 0

def apply_lazy(node, val):
    node.value += val
    node.path_sum += val * node.size
    node.path_max += val
    node.lazy_add += val

📌 lazy_add와 rev는 같이 관리해야 하며,
access → splay → push → update 순서가 필수입니다.


🛠 Path 쿼리 실행 플로우

1. path_sum(u, v)

def path_sum(u, v):
    make_root(u)
    access(v)
    splay(v)
    return v.path_sum

2. path_max(u, v)

def path_max(u, v):
    make_root(u)
    access(v)
    splay(v)
    return v.path_max

3. update_node(u, val)

def update_node(u, val):
    access(u)
    splay(u)
    u.value = val
    update(u)

4. update_path(u, v, add_val)

def update_path(u, v, add_val):
    make_root(u)
    access(v)
    splay(v)
    apply_lazy(v, add_val)

✅ 실전 예시

💡 예: 노드 초기화 및 질의

# 초기화
nodes = [LCTNode(1) for _ in range(5)]
link(nodes[0], nodes[1])
link(nodes[1], nodes[2])
link(nodes[2], nodes[3])

# u–v 경로합
print(path_sum(nodes[0], nodes[3]))  # 4

# u–v 경로 최댓값
print(path_max(nodes[0], nodes[3]))  # 1

# 경로값 일괄 증가
update_path(nodes[0], nodes[3], 2)

# 다시 확인
print(path_sum(nodes[0], nodes[3]))  # 12
print(path_max(nodes[0], nodes[3]))  # 3

📘 실전 문제에서의 활용

문제 번호 활용 포인트

Codeforces 1175F 경로 최대값 유지 + edge 교체
Library Contest – Range LCT path add + path max 혼합
AtCoder AGC Tree Contest update_path + path sum

📌 정리 요약

  • LCT에서 경로 질의는 make_root(u) → access(v) → splay(v) 패턴으로 실행
  • 합, 최댓값, 일괄 변경 등은 push/update/apply_lazy를 제대로 구성하면 모두 가능
  • Lazy Propagation은 필수 옵션으로 복잡한 쿼리에도 대응 가능

👉 다음 글에서는 Euler Tour Tree 기반의 노드/경로 색칠 및 다이나믹 라벨링 문제를 다루겠습니다.
(예: 경로 중 빨간 노드 개수 구하기, 서브트리 내 라벨 통계 등)


📚 참고자료

  • CP-Algorithms – Link/Cut Tree augmentation
  • Sedgewick Algorithms (BST Augmentation Techniques)
  • Stanford CS166 – Dynamic Tree Applications
  • Sleator & Tarjan (1985) “Self-adjusting Binary Search Trees with Augmentation”

 

LinkCutTree, 경로합, 경로최댓값, 트리갱신, LazyPropagation, 동적트리, pathQuery, updatePath, 파이썬알고리즘, SEO 최적화 10개