🌳 Heavy-Light Decomposition (HLD) – Comprehensive Study Guide
**Heavy-Light Decomposition (HLD)** is a **tree decomposition technique** that allows us to efficiently process **path queries and updates** in **O(log² N)** time using **Segment Tree or Fenwick Tree**.
⚡ Why Use HLD?
- ✅ **Fast path queries in O(log² N) instead of O(N).**
- 🚀 **Efficiently handles dynamic updates in trees.**
- 🔍 **Useful in Lowest Common Ancestor (LCA) queries.**
- 📡 **Essential for competitive programming (ICPC, Codeforces, AtCoder).**
📌 Understanding Heavy & Light Edges
📥 **1. Heavy Edges**
The **edge connecting a parent node** to the **child node with the largest subtree**.
📤 **2. Light Edges**
All other edges that do not belong to the heavy path.
📌 Breaking the Tree into Heavy & Light Paths
📌 Step-by-Step Process:
- ✅ Find the **size of each subtree**.
- 📌 Identify the **child with the largest subtree** (Heavy Child).
- 🔄 Connect all **heavy children** into **heavy paths**.
- 🔗 Light edges remain separate & can be handled separately.
🛠️ Heavy-Light Decomposition Implementation
📌 **1. DFS to Compute Subtree Sizes**
vector> tree;
vector parent, depth, subtreeSize, heavyChild;
int dfs(int node, int par) {
parent[node] = par;
subtreeSize[node] = 1;
int maxSubtree = 0;
for (int child : tree[node]) {
if (child == par) continue;
depth[child] = depth[node] + 1;
int childSize = dfs(child, node);
subtreeSize[node] += childSize;
if (childSize > maxSubtree) {
maxSubtree = childSize;
heavyChild[node] = child;
}
}
return subtreeSize[node];
}
📌 **2. Building Heavy-Light Chains**
vector chainHead, positionInBase, baseArray;
int chainCount = 0, pos = 0;
void hld(int node, int head) {
chainHead[node] = head;
positionInBase[node] = pos++;
baseArray.push_back(node);
if (heavyChild[node] != -1)
hld(heavyChild[node], head);
for (int child : tree[node]) {
if (child != parent[node] && child != heavyChild[node])
hld(child, child); // Start a new chain
}
}
📏 Path Queries Using Segment Tree
📌 **1. Updating Values in a Path (O(log² N))**
int queryPath(int u, int v) {
int result = 0;
while (chainHead[u] != chainHead[v]) {
if (depth[chainHead[u]] < depth[chainHead[v]]) swap(u, v);
result += segmentTree.query(positionInBase[chainHead[u]], positionInBase[u]);
u = parent[chainHead[u]];
}
if (depth[u] > depth[v]) swap(u, v);
result += segmentTree.query(positionInBase[u], positionInBase[v]);
return result;
}
📌 **2. Finding Lowest Common Ancestor (LCA) Using HLD**
int LCA(int u, int v) {
while (chainHead[u] != chainHead[v]) {
if (depth[chainHead[u]] < depth[chainHead[v]]) swap(u, v);
u = parent[chainHead[u]];
}
return (depth[u] < depth[v]) ? u : v;
}
⏳ Time Complexity Analysis
Operation | Time Complexity |
---|---|
Preprocessing (DFS & HLD) | O(N) |
Path Query | O(log² N) |
Point Update | O(log N) |
Lowest Common Ancestor (LCA) | O(log N) |
🌍 Real-World Applications of HLD
- 📡 **Network Routing** – Fast processing of **network queries**.
- 🎮 **Game Development** – Efficient **region queries** in maps.
- 🔄 **Tree-Based DP Problems** – Queries on **subtree sums**.
- 🗂 **File System Optimization** – Managing **hierarchical structures**.