Purpose: Maintain a forest under link and cut, supporting path queries and updates in amortized. Better known as link-cut trees β Sleator and Tarjan, 1983.
Operations
| Operation | Meaning |
|---|---|
makeTree(v) | create a single-vertex tree |
link(u, v) | attach tree of (as root) under |
cut(v) | detach from its parent |
findRoot(v) | root of βs tree |
pathAggregate(u, v) | sum / min / max along the path |
pathUpdate(u, v, x) | add to every edge on the path |
lca(u, v) | lowest common ancestor |
evert(v) | make the root (reverse a path) |
All amortized.
The Structure: preferred paths + splay trees
- Decompose the tree into preferred paths β vertex-disjoint downward paths covering every vertex (like HLD, but the decomposition changes dynamically with access patterns).
- Store each preferred path in a splay tree, keyed implicitly by depth.
- Link the splay trees together with path-parent pointers: the root of one pathβs splay tree points to the vertex it hangs off in the parent path (a one-way pointer, invisible to the splay tree itself).
access(v) β the one primitive
Everything is built on access(v), which makes the path from the root to a single preferred path with at its bottom:
access(v):
splay(v)
detach v's right subtree (deeper vertices leave the preferred path)
while v has a path-parent w:
splay(w)
replace w's right subtree with v's splay tree
rotate v up
Then:
findRoot(v)=access(v), then walk left to the shallowest vertex;pathAggregate(u,v)=evert(u),access(v), read the splay treeβs aggregate;link(u,v)=evert(u), then set βs path-parent to ;cut(v)=access(v), detach the left subtree.
Complexity
amortized per operation. The proof uses a heavy-light argument to bound the number of preferred-child changes at per access, plus the splay tree access lemma. It is not worst-case: a single operation can cost , though a sequence of cannot cost more than .
Link-cut trees vs the alternatives
| Structure | Handles | Path aggregates | Difficulty |
|---|---|---|---|
| HLD + segment tree | static tree | yes | moderate |
| Euler tour tree | link/cut | subtree only | moderate |
| Link-cut tree | link/cut | yes | hard |
| Top tree | link/cut | yes, plus non-local info | very hard |
Do you actually need it?
If the tree is static, use HLD β simpler, worst-case , and the segment tree gives you lazy updates for free. If updates are offline, consider offline dynamic connectivity instead. Reach for link-cut trees when the tree genuinely changes online and you need path queries.
What it unlocks
- Dynamic connectivity in forests β per operation
- Max flow speedups β Dinic with link-cut trees is ; see Goldberg-Tarjan
- Online MST maintenance β path-max query plus edge swap
- LCA in a changing tree
- Offline problems in disguise β sometimes it is the only structure that fits
Variants / Use Cases
- Link-Cut Tree β the topic page with implementation notes
- Euler Tour Trees β for subtree, not path, aggregates
- Top trees / topology trees β the more general framework
- Splay trees β the self-adjusting BST underneath; Sleator and Tarjan invented those too