Link-Cut Tree

概要

動的木のデータ構造

Heavy-Light Decompositionを動的にしたイメージ。 分解したパスはSplay Tree上で管理する。

以下の操作ができる

  • expose(i): 根頂点から頂点iまでのパスのみを繋げる (それ以外のパスをカット

  • cut(i): 木を頂点iと頂点iの親頂点pの間の辺をカットして分割

  • link(i, p): 2つの木について、頂点iと頂点pを接続して1つの木にする

  • evert(i): 木の根頂点を頂点iに変更

計算量

  • 各クエリ: \(O(\log N)\)

実装

各ノードの情報を別の配列で管理する実装。 各頂点は1-indexedで番号付けして管理する。

Python3が遅い。PyPy3はある程度高速。

# prt[i]: 頂点iの親頂点
# left[i]: 頂点iの左部分木, right[i]: 頂点iの右部分木
# sz[i]: 頂点iを根頂点とする部分木のサイズ
# key[i]: 頂点iが持つ値
# val[i]: 頂点i以下の部分木の持つ値の和
# rev[i]: 木の反転遅延が行われているか
N = 10**5
prt = [0]*(N+1)
left = [-1] + [0]*N
right = [-1] + [0]*N
sz = [0] + [1]*N
key = [0]*(N+1)
val = [0]*(N+1)
rev = [0]*(N+1)

def update(i, l, r):
    #assert 1 <= i <= N
    sz[i] = 1 + sz[l] + sz[r]
    val[i] = key[i] + val[l] + val[r]

def swap(i):
    if i:
        left[i], right[i] = right[i], left[i]
        rev[i] ^= 1

def prop(i):
    swap(left[i])
    swap(right[i])
    rev[i] = 0
    return 1

def splay(i):
    #assert 1 <= i <= N
    x = prt[i]
    rev[i] and prop(i)

    li = left[i]; ri = right[i]
    while x and not left[x] != i != right[x]:
        y = prt[x]
        if not y or left[y] != x != right[y]:
            if rev[x] and prop(x):
                li, ri = ri, li
                swap(li); swap(ri)

            if left[x] == i:
                left[x] = ri
                prt[ri] = x
                update(x, ri, right[x])
                ri = x
            else:
                right[x] = li
                prt[li] = x
                update(x, left[x], li)
                li = x
            x = y
            break

        rev[y] and prop(y)
        if rev[x] and prop(x):
            li, ri = ri, li
            swap(li); swap(ri)

        z = prt[y]
        if left[y] == x:
            if left[x] == i:
                v = left[y] = right[x]
                prt[v] = y
                update(y, v, right[y])

                left[x] = ri; right[x] = y
                prt[ri] = x
                update(x, ri, y)

                prt[y] = ri = x
            else:
                left[y] = ri
                prt[ri] = y
                update(y, ri, right[y])

                right[x] = li
                prt[li] = x
                update(x, left[x], li)

                li = x; ri = y
        else:
            if right[x] == i:
                v = right[y] = left[x]
                prt[v] = y
                update(y, left[y], v)

                left[x] = y; right[x] = li
                prt[li] = x
                update(x, y, li)

                prt[y] = li = x
            else:
                right[y] = li
                prt[li] = y
                update(y, left[y], li)

                left[x] = ri
                prt[ri] = x
                update(x, ri, right[x])

                li = y; ri = x

        x = z
        if left[z] == y:
            left[z] = i
            update(z, i, right[z])
        elif right[z] == y:
            right[z] = i
            update(z, left[z], i)
        else:
            break

    update(i, li, ri)
    left[i] = li; right[i] = ri
    prt[li] = prt[ri] = i
    prt[i] = x

    rev[i] = prt[0] = 0

def expose(i):
    p = 0
    cur = i
    while cur:
        splay(cur)
        right[cur] = p
        update(cur, left[cur], p)
        p = cur
        cur = prt[cur]
    splay(i)
    return p

def cut(i):
    expose(i)
    p = left[i]
    left[i] = prt[p] = 0
    return p

def link(i, p):
    expose(i)
    expose(p)
    prt[i] = p
    right[p] = i

def evert(i):
    expose(i)
    swap(i)
    rev[i] and prop(i)

def root(u):
    expose(u)
    while left[u]:
        u = left[u]
    splay(u)
    return u

def lca(u, v):
    expose(u)
    while left[u]:
        u = left[u]
    w = expose(v)
    while left[v]:
        v = left[v]
    return w if u == v else None

Verified

  • AOJ: "GRL_5_D: Tree - Range Query on a Tree": source (Python3, 9.37sec)

  • AtCoder: "2013年 日本情報オリンピック春合宿 4日目: 3 - 宇宙船 (Spaceships)": source (PyPy3, 8114ms)

その他の実装

1ノードを1つのlistで管理する実装

Python3だと高速化するが、PyPy3がPython3よりも遅くなる。

  • AOJ: "GRL_5_D: Tree - Range Query on a Tree": source (Python3, 7.80sec)

参考ページ