Tree Decompositions & Dynamic Trees
Heavy-Light Decomposition (HLD) mapping tree paths to Segment Tree intervals in O(log² n), Centroid Decomposition, and Link-Cut Trees.
Complex tree queries and dynamic forest topology mutations require advanced structural partitioning beyond standard depth-first traversals. By decomposing trees into vertex-disjoint heavy paths, recursive centroid hierarchies, or splay-backed preferred paths, algorithms execute path aggregations, dynamic connectivity updates, and distance divide-and-conquer in poly-logarithmic time.
1. Executive Summary & Learning Objectives
#This module introduces advanced structural decompositions for tree-structured data, bridging static tree algorithms and dynamic geometric graphs.
By the end of this chapter, you will be able to:
- Decompose Trees via HLD: Partition general tree topologies into vertex-disjoint Heavy Paths such that any simple path crosses at most Light Edges.
- Linearize Path Queries onto Segment Trees: Coordinate heavy-path head jumps with segment tree interval queries to achieve path sum/max operations.
- Execute Centroid Divide-and-Conquer: Recursively isolate balance centroids to construct a -depth centroid tree, evaluating all tree paths in total time.
- Maintain Dynamic Forests via Link-Cut Trees: Utilize splay-backed preferred path decompositions to execute
Link,Cut, and path aggregate queries in amortized time.
2. Topic 154b & 154c: Heavy-Light Decomposition (HLD)
#1. Conceptual Motivation
Given a tree with nodes subject to dynamic vertex updates, standard algorithms face severe trade-offs:
- Naive DFS path scans require time per query.
- Flattening via Euler Tour handles subtree updates in , but cannot efficiently handle arbitrary path queries between arbitrary nodes and .
Heavy-Light Decomposition (HLD) addresses this by decomposing the tree into vertex-disjoint chains (Heavy Paths) such that any path from the root to an arbitrary node passes through at most distinct light edges.
2. Heavy vs. Light Edge Classification
#For every internal node :
- Compute subtree sizes for each child of .
- The child with the strictly largest subtree is designated the Heavy Child (breaking ties arbitrarily).
- The directed edge is a Heavy Edge.
- All other edges emanating from to remaining children are Light Edges.
| Edge Classification | Criterion | Subtree Size Condition | Transition Cost |
|---|---|---|---|
| Heavy Edge | Maximum child subtree: | (at most one per node) | Traversed along contiguous segment tree array block |
| Light Edge | Any non-heavy child | Requires jumping across chains via parent pointers |
3. The Fundamental HLD Theorem
#Proof:
By definition, crossing a light edge implies . If , then would possess more than half the total descendants of , making it impossible for any sibling to exceed it, forcing to be the heavy child.
Since the subtree size strictly halves upon traversing every light edge, one can cross at most light edges before the subtree size reduces to 1.
4. Implementation: Heavy-Light Decomposition Path Queries
#export class HeavyLightDecomposition {
private n: number;
private adj: number[][];
private parent: number[];
private depth: number[];
private heavy: number[];
private head: number[];
private pos: number[];
private curPos: number = 0;
constructor(n: number, adj: number[][], root: number = 0) {
this.n = n;
this.adj = adj;
this.parent = new Array(n).fill(-1);
this.depth = new Array(n).fill(0);
this.heavy = new Array(n).fill(-1);
this.head = new Array(n).fill(0);
this.pos = new Array(n).fill(0);
this.dfsSize(root, -1, 0);
this.decompose(root, root);
}
// Pass 1: Compute subtree sizes, depths, and identify heavy edges
private dfsSize(u: number, p: number, d: number): number {
this.parent[u] = p;
this.depth[u] = d;
let size = 1;
let maxChildSize = 0;
for (const v of this.adj[u]) {
if (v !== p) {
const childSize = this.dfsSize(v, u, d + 1);
size += childSize;
if (childSize > maxChildSize) {
maxChildSize = childSize;
this.heavy[u] = v; // Heavy child
}
}
}
return size;
}
// Pass 2: Assign contiguous positions to nodes on the same heavy chain
private decompose(u: number, h: number): void {
this.head[u] = h;
this.pos[u] = this.curPos++;
// Continue the heavy chain first to ensure contiguous array indices
if (this.heavy[u] !== -1) {
this.decompose(this.heavy[u], h);
}
// Decompose light child subtrees as new chain heads
for (const v of this.adj[u]) {
if (v !== this.parent[u] && v !== this.heavy[u]) {
this.decompose(v, v);
}
}
}
/**
* Decomposes the path between u and v into at most O(log n) contiguous segments.
*/
public queryPath(
u: number,
v: number,
segmentQuery: (l: number, r: number) => number
): number {
let result = 0;
while (this.head[u] !== this.head[v]) {
if (this.depth[this.head[u]] < this.depth[this.head[v]]) {
[u, v] = [v, u];
}
// Query contiguous segment on u's heavy chain
result += segmentQuery(this.pos[this.head[u]], this.pos[u]);
u = this.parent[this.head[u]]; // Jump across light edge
}
// u and v are now on the same heavy chain
if (this.depth[u] > this.depth[v]) {
[u, v] = [v, u];
}
result += segmentQuery(this.pos[u], this.pos[v]);
return result;
}
}3. Topic 154d: Centroid Decomposition
#1. Definition of a Tree Centroid
#A Centroid of an unrooted tree with is a vertex whose removal partitions the tree into a forest where every resulting connected component has size at most :
Existence Theorem: Every finite tree possesses at least one and at most two centroids. A centroid can be located in time via a single depth-first search:
export function findCentroid(
u: number,
parent: number,
totalSize: number,
adj: number[][],
subtreeSize: number[],
isRemoved: boolean[]
): number {
for (const v of adj[u]) {
if (v !== parent && !isRemoved[v]) {
if (subtreeSize[v] > totalSize / 2) {
return findCentroid(v, u, totalSize, adj, subtreeSize, isRemoved);
}
}
}
return u; // u is the centroid
}2. Centroid Divide-and-Conquer Architecture
#- Locate the centroid of the current component.
- Evaluate all paths traversing through (combining information across distinct subtrees).
- Mark as removed (
isRemoved[C] = true). - Recursively decompose each disconnected remaining subtree.
- Connecting each child centroid to its parent centroid constructs the Centroid Tree.
- Height Invariant: Because component sizes reduce by at least a factor of 2 at each recursive level, the depth of the centroid tree is bounded by .
- Complexity: Overall divide-and-conquer processing executes in time.
4. Topic 154e: Link-Cut Trees (Dynamic Forest Algorithms)
#Developed by Daniel Sleator and Robert Tarjan in 1983, the Link-Cut Tree (LCT) maintains a collection of disjoint rooted trees subject to online structural modifications:
| Operation | Semantics | Amortized Time Complexity |
|---|---|---|
Link(u, v) | Adds a directed edge making a child of | |
Cut(u) | Severs the edge connecting to its parent | |
FindRoot(u) | Identifies the root of the tree containing | |
PathQuery(u, v) | Aggregates edge/vertex weights along the simple path between and |
Splay-Backed Preferred Path Decomposition
#Unlike HLD where heavy edges remain statically fixed based on initial tree structure, a Link-Cut Tree utilizes Preferred Edges that mutate dynamically:
- Whenever a node is accessed via
access(u), the path from the root of 's tree to becomes the single active preferred path. - Each preferred path is stored in an auxiliary Splay Tree ordered by vertex depth.
- The
access(u)primitive splays nodes across auxiliary tree boundaries, ensuring that all subsequent structural operations execute in amortized time.
5. Summary & Comparison of Advanced Tree Techniques
#| Technique | Dynamic Edits Supported? | Path Aggregates | Subtree Aggregates | Implementation Complexity |
|---|---|---|---|---|
| Euler Tour | No (Static tree) | Low | ||
| Binary Lifting (LCA) | No (Static tree) | Not supported | Low | |
| Heavy-Light Decomposition | Vertex values only | Moderate | ||
| Centroid Decomposition | No (Static tree) | All-pairs distance in | Not supported | Moderate |
| Link-Cut Tree | Yes (Link & Cut) | Complex (Requires auxiliary trees) | High |
References & Academic Attribution
#- Sleator, D. D., & Tarjan, R. E. (1983). A data structure for dynamic trees. Journal of Computer and System Sciences, 26(3), 362–391.
- Tarjan, R. E. (1979). Applications of path compression on balanced trees. Journal of the ACM (JACM), 26(4), 690–715.
- Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2022). Introduction to Algorithms (4th ed.). MIT Press.