#include<bits/stdc++.h> #define ll long long // NOTE: // 1-INDEXED // Usage: // HLD hld; // hld.init(n); // hld.addEdge(u, v); // hld.decompose(root); // build segment tree using inarray_inv[u] // hld.getLCA(u, v) // we can call getHLPath(u, v) to get all {from, to} path that we must walk through when travelling from u to v struct HLD // Heavy Light Decomposition { int n; int *sz; int *next_heavy; int *depth; int *pa; int *head_heavy; int *tail_heavy; int *inarray; // inarray[u] is where node u-th resides in the array to make segment tree (tree-node to array-index) int *inarray_inv; // use this to build/init segment tree (array-index to tree-node) vector<int> *adj; HLD() {}; void init(int n_) { n = n_; sz = new int[n+1]; next_heavy = new int[n+1]; depth = new int[n+1]; pa = new int[n+1]; head_heavy = new int[n+1]; tail_heavy = new int[n+1]; inarray = new int[n+1]; inarray_inv = new int[n+1]; adj = new vector<int>[n+1]; fill(sz, sz + n+1, 0); fill(next_heavy, next_heavy + n+1, -1); fill(depth, depth + n+1, 0); fill(pa, pa + n+1, 0); fill(head_heavy, head_heavy + n+1, -1); fill(tail_heavy, tail_heavy + n+1, -1); fill(inarray, inarray + n+1, -1); fill(inarray_inv,inarray_inv + n+1,-1); } void addEdge(int u, int v) { adj[u].push_back(v); adj[v].push_back(u); } void dfs(int u, int p) { pa[u] = p; sz[u] = 1; depth[u] = depth[p] + 1; int max_size = 0; int best_child = -1; for (auto v : adj[u]) if (v != p) { dfs(v, u); if (sz[v] > max_size) { max_size = sz[v]; best_child = v; } sz[u] += sz[v]; } if (best_child > 0) { next_heavy[u] = best_child; } } void dfs2(int u, int h, int &cnt) { head_heavy[u] = h; inarray[u] = cnt++; inarray_inv[cnt-1] = u; if (next_heavy[u] > 0) { dfs2(next_heavy[u], h, cnt); tail_heavy[u] = tail_heavy[next_heavy[u]]; } else { tail_heavy[u] = u; } for (auto v : adj[u]) if (v != pa[u] && v != next_heavy[u]) { dfs2(v, v, cnt); } } void decompose(int root) { int cnt = 1; depth[root] = 1; dfs (root, 0); dfs2(root, root, cnt); } int getLCA(int u, int v) { while(u != v) { if (depth[u] > depth[v]) swap(u, v); if (head_heavy[u] == head_heavy[v]) { v = u; break; } if (depth[head_heavy[v]] > depth[head_heavy[u]]) v = pa[head_heavy[v]]; else u = pa[head_heavy[u]]; } return u; } void travelHL(int child, int parent, vector< pair<int, int> > & paths, bool include_parent) { // get paths when travelling from a child to its parent while(head_heavy[child] != head_heavy[parent]) { paths.push_back({inarray[child], inarray[head_heavy[child]]}); child = pa[head_heavy[child]]; } if (include_parent) { paths.push_back({inarray[child], inarray[parent]}); } else { if (child != parent) { int x = next_heavy[parent]; paths.push_back({inarray[child], inarray[x]}); } } } vector< pair<int, int> > getHLPath(int u, int v) { // returns a vector of {x, y} where we should travel from index x to index y in the ARRAY // NOTE: we must call buildLCA() before calling this function vector< pair<int, int> > paths; int lca = getLCA(u, v); travelHL(u, lca, paths, true); travelHL(v, lca, paths, false); return paths; } };