import java.util.*; import java.util.stream.Stream; // Answering LCA queries in O(1) with O(n*log(n)) preprocessing public class LcaSparseTable { int len; int[][] up; int[] tin; int[] tout; int time; void dfs(List[] tree, int u, int p) { tin[u] = time++; up[0][u] = p; for (int i = 1; i < len; i++) up[i][u] = up[i - 1][up[i - 1][u]]; for (int v : tree[u]) if (v != p) dfs(tree, v, u); tout[u] = time++; } public LcaSparseTable(List[] tree, int root) { int n = tree.length; len = 1; while ((1 << len) <= n) ++len; up = new int[len][n]; tin = new int[n]; tout = new int[n]; dfs(tree, root, root); } boolean isParent(int parent, int child) { return tin[parent] <= tin[child] && tout[child] <= tout[parent]; } public int lca(int a, int b) { if (isParent(a, b)) return a; if (isParent(b, a)) return b; for (int i = len - 1; i >= 0; i--) if (!isParent(up[i][a], b)) a = up[i][a]; return up[0][a]; } // Usage example public static void main(String[] args) { List[] tree = Stream.generate(ArrayList::new).limit(5).toArray(List[]::new); tree[0].add(1); tree[1].add(0); tree[1].add(2); tree[2].add(1); tree[3].add(1); tree[1].add(3); tree[0].add(4); tree[4].add(0); LcaSparseTable t = new LcaSparseTable(tree, 0); System.out.println(1 == t.lca(3, 2)); System.out.println(0 == t.lca(2, 4)); } }