/* A Tree and Two Edges * * First, find a spanning tree of the graph. Then, since there are only two * extra edges, we can try every combination of their order and orientation in * a simple path from s to t. To ensure the path is simple, make sure no * segments intersect, Checking if two paths in a tree intersect can be done by * computing fast LCAs. */ #include #include #include #include #include struct TreeInfo { TreeInfo(const auto& tree, int32_t root = 0) : n(tree.size()), timestamp(0), depth(n), timeIn(n), timeOut(n), vis(n, false) { depth[root] = 0; for (logn = 0; (1 << logn) <= n;) ++logn; sparseTable.assign(logn, std::vector(n, -1)); dfs(tree, root, -1); assert(bonusIdx == 2); assert(timestamp == n); for (size_t j = 0; j + 1 < logn; ++j) { for (size_t u = 0; u < n; ++u) { if (sparseTable[j][u] != -1) { sparseTable[j + 1][u] = sparseTable[j][sparseTable[j][u]]; } } } } void dfs(const auto& tree, int32_t u, int32_t p) { vis[u] = true; timeIn[u] = timestamp++; for (auto v : tree[u]) if (v != parent(u)) { if (!vis[v]) { sparseTable[0][v] = u; depth[v] = depth[u] + 1; dfs(tree, v, u); } else if (v != p && u < v) { bonus[bonusIdx++] = {u, v}; } } timeOut[u] = timestamp; } int32_t lca(int32_t u, int32_t v) const { if (depth[u] > depth[v]) { std::swap(u, v); } for (int32_t j = logn - 1; j >= 0; --j) { if (depth[v] - (1 << j) >= depth[u]) { v = sparseTable[j][v]; } } assert(depth[u] == depth[v]); assert(u != -1 && v != -1); if (u == v) { return u; } for (int32_t j = logn - 1; j >= 0; --j) { if (sparseTable[j][u] != sparseTable[j][v]) { u = sparseTable[j][u]; v = sparseTable[j][v]; } } return sparseTable[0][u]; } inline int32_t parent(int32_t u) const { return sparseTable[0][u]; } bool doPathsIntersect(int32_t u1, int32_t v1, int32_t u2, int32_t v2) { int32_t w1 = lca(u1, v1); int32_t w2 = lca(u2, v2); return onPath(w1, w2, u2) || onPath(w1, w2, v2) || onPath(w2, w1, u1) || onPath(w2, w1, v1); }; // is u in between up and down where up is an ancestor of down inline bool onPath(int32_t u, int32_t up, int32_t down) { assert(inSubtree(up, down)); return inSubtree(up, u) && inSubtree(u, down); } // is v in the subtree of u inline bool inSubtree(int32_t u, int32_t v) const { return (timeIn[u] <= timeIn[v]) && (timeIn[v] < timeOut[u]); } int32_t n, root, logn, timestamp; // depth[u] - number of edges between 0 and u std::vector depth, timeIn, timeOut; // st[j][u] = 2^jth ancestor of u std::vector> sparseTable; // bonus edges std::vector vis; std::array, 2> bonus{}; int32_t bonusIdx = 0; }; int main() { int32_t n, q; std::cin >> n >> q; std::vector> tree(n); for (auto i = 0; i < n + 1; ++i) { int32_t u, v; std::cin >> u >> v; --u; --v; tree[u].push_back(v); tree[v].push_back(u); } TreeInfo info{tree}; const auto& bonus = info.bonus; while (q-- > 0) { int32_t u, v; std::cin >> u >> v; --u; --v; // zero edge int32_t ans = 1; // one edge for (auto k = 0; k < 2; ++k) { ans += !info.doPathsIntersect(u, bonus[k][0], bonus[k][1], v); ans += !info.doPathsIntersect(u, bonus[k][1], bonus[k][0], v); } // two edges // order of edges for (auto j = 0; j < 2; ++j) { // first edge orientation for (auto k = 0; k < 2; ++k) { // second edge orientation for (auto l = 0; l < 2; ++l) { std::array order{u, bonus[j][k], bonus[j][k^1], bonus[j^1][l], bonus[j^1][l^1], v}; ans += !info.doPathsIntersect(order[0], order[1], order[2], order[3]) && !info.doPathsIntersect(order[0], order[1], order[4], order[5]) && !info.doPathsIntersect(order[2], order[3], order[4], order[5]); } } } std::cout << ans << '\n'; } return 0; }