import java.util.*; /** * Start by constructing a tree on all nodes and rooting the tree at some node. * There will be two edges left over, A and B. * For each query there will be at least one path and at most 4 paths. * The possibilities for this path are: * 1. Use neither edge A nor edge B. Such a path always exists. * 2. Use edge A but not edge B. * 3. Use edge B but not edge A. * 4. Use edge A and edge B. * For each of these possibilities we need to check if there is a simple path. * For a query between nodes u and v and case (2) we check if the path * from u to an endpoint of A is node disjoint with the path from the other * endpoint of A to v. Do this for both choices endpoints of A. Case (3) is * similar. Case (4) requires checking each possible traversal order of edges * A and B. There are 8 such orders. * To determine if a path from u to v is vertex disjoint from a path from x to y, * it suffices to check if lca(u, v) is on the path from x to y and if lca(x, y) * is on the path from u to v. Use binary lifting to compute LCA in log(n) time. * Time Complexity: O((n+q)(log n)) * * @author Finn Lidbetter */ public class tree_finn { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int q = sc.nextInt(); Node[] nodes = new Node[n]; for (int i=0; i order = new ArrayList<>(); rootTreeDFS(nodes, 0, order); int maxDepth = 0; for (int i=0; i maxDepth) { maxDepth = nodes[i].depth; } } int skip = 1; int skipPow = 0; while (skip < maxDepth) { skip *= 2; skipPow++; } int[][] ancestor = new int[n][skipPow+1]; for (int nodeId: order) { ancestor[nodeId][0] = nodes[nodeId].parent; } for (int i=1; i=0) { if (ancestor[u][shiftIndex]==ancestor[v][shiftIndex]) { shiftIndex--; } else { u = ancestor[u][shiftIndex]; v = ancestor[v][shiftIndex]; } } if (u==v) { return u; } return nodes[u].parent; } static int nthParent(int u, int n, int[][] ancestor) { int mask = 1; int shifted = 0; int curr = u; while (mask<=n) { if (curr == -1) { return -1; } if ((n&mask) > 0) { curr = ancestor[curr][shifted]; } mask = mask << 1; shifted++; } return curr; } /** * Return True iff x is on the path between u and v. */ static boolean nodeOnPath(int u, int v, int x, Node[] nodes, int[][] ancestor) { int lca = lca(u, v, nodes, ancestor); if (u==x || v==x || lca == x) { return true; } if (nodes[u].depth >= nodes[x].depth && nodes[x].depth >= nodes[lca].depth) { if (nthParent(u, nodes[u].depth - nodes[x].depth, ancestor) == x) { return true; } } if (nodes[v].depth >= nodes[x].depth && nodes[x].depth >= nodes[lca].depth) { if (nthParent(v, nodes[v].depth - nodes[x].depth, ancestor) == x) { return true; } } return false; } static void rootTreeDFS(Node[] nodes, int curr, ArrayList order) { order.add(curr); for (int nbr: nodes[curr].adj) { if (nbr != nodes[curr].parent) { nodes[nbr].depth = nodes[curr].depth + 1; nodes[nbr].parent = curr; nodes[curr].children.add(nbr); rootTreeDFS(nodes, nbr, order); } } } } class Edge { int u,v; public Edge(int uu, int vv) { u = uu; v = vv; } } class Node { int id; int depth; int parent; ArrayList adj; ArrayList children; public Node(int idd) { id = idd; depth = 0; parent = -1; adj = new ArrayList<>(); children = new ArrayList<>(); } } class UnionFind { int[] id; public UnionFind(int n) { id = new int[n]; for (int i = 0; i < n; i++) { id[i] = i; } } int find(int p) { int rt = p; while (rt != id[rt]) { rt = id[rt]; } while (p != rt) { int next = id[p]; id[p] = rt; p = next; } return rt; } boolean connected(int p, int q) { return find(p) == find(q); } void union(int p, int q) { int r1 = find(p); int r2 = find(q); if (r1 == r2) { return; } id[r1] = r2; } }