#line 1 "test/graph/bcc/yuki1326.test.cpp" #define PROBLEM "https://yukicoder.me/problems/no/1326" #line 2 "src/Template.hpp" #define CUT #include using namespace std; #define rep(i, l, r) for (int i = (l); i < (r); ++i) #define rrep(i, l, r) for (int i = (r); i --> (l);) #define all(c) begin(c), end(c) #ifdef LOCAL #define debug(...) debug_impl(#__VA_ARGS__, __VA_ARGS__) template void debug_impl(string s, H&& h, Ts&&... ts) { cerr << '(' << s << "): (" << forward(h); ((cerr << ", " << forward(ts)), ..., (cerr << ")\n")); } #else #define debug(...) void(0) #endif template bool chmax(T& a, const T& b) { return b > a ? (a = b, true) : false; } template bool chmin(T& a, const T& b) { return b < a ? (a = b, true) : false; } template istream& operator>>(istream& in, vector& v) { for (auto& e : v) in >> e; return in; } template void read(Args&... args) { (cin >> ... >> args); } template ostream& operator<<(ostream& out, const vector& v) { int n = v.size(); rep(i, 0, n) { out << v[i]; if (i + 1 != n) out << ' '; } return out; } template void print(H&& h, Ts &&... ts) { cout << h, ((cout << ' ' << forward(ts)), ..., (cout << '\n')); } struct io_setup_ { io_setup_() { ios::sync_with_stdio(false), cin.tie(nullptr); cout << fixed << setprecision(10); } } io_setup{}; #undef CUT #define NOTE compile command: \texttt{g++ -std=gnu++17 -Wall -Wextra -g -fsanitize=address -fsanitize=undefined \$\{file\} -o \$\{fileDirname\}/\$\{fileBasenameNoExtension\}} #undef NOTE #define NOTE \texttt{-DLOCAL} を加えると \texttt{debug(...)} による出力が有効となる #undef NOTE #line 3 "src/tree/HLD.hpp" #define CUT struct HLD { int n; vector visit, leave, head, ord, siz, par, dep; private: int dfs(vector>& g, int u, int p) { par[u] = p; int maxsiz = 0; for (int& v : g[u]) if (v != p) { dep[v] = dep[u] + 1; siz[u] += dfs(g, v, u); if (chmax(maxsiz, siz[v])) swap(g[u][0], v); } return siz[u]; } int time = 0; void hld(const vector>& g, int u, int p) { visit[u] = time, ord[time] = u, ++time; head[u] = p >= 0 and g[p][0] == u ? head[p] : u; for (int v : g[u]) if (v != p) hld(g, v, u); leave[u] = time; } public: HLD() = default; HLD(vector>& g, vector roots = {}): n(g.size()), visit(n), leave(n), head(n), ord(n), siz(n, 1), par(n, -1), dep(n) { for (int r : roots) if (par[r] < 0) dfs(g, r, -1); rep(r, 0, n) if (par[r] < 0) dfs(g, r, -1); rep(r, 0, n) if (par[r] < 0) hld(g, r, -1); } int size() const { return n; } int lca(int u, int v) const { for (;; v = par[head[v]]) { if (visit[u] > visit[v]) swap(u, v); if (head[u] == head[v]) return u; } } int la(int u, int k) const { if (k < 0) return -1; while (u >= 0) { int h = head[u]; if (visit[u] - k >= visit[h]) return ord[visit[u] - k]; k -= visit[u] - visit[h] + 1; u = par[h]; } return -1; } int jump(int u, int v, int d) const { if (d < 0) return -1; int w = lca(u, v); int uw = dep[u] - dep[w]; if (d <= uw) return la(u, d); int vw = dep[v] - dep[w]; return d <= uw + vw ? la(v, (uw + vw) - d) : -1; } int dist(int u, int v) const { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; } vector> get_path(int u, int v, bool is_edge_query) const { vector> res; for (;; v = par[head[v]]) { if (visit[u] > visit[v]) swap(u, v); if (head[u] == head[v]) break; res.emplace_back(visit[head[v]], visit[v] + 1); } res.emplace_back(visit[u] + is_edge_query, visit[v] + 1); return res; } // uvパス上の値を順番通りに掛けた結果を返す template T fold_path_ordered(int u, int v, Op op, T e, Prod prod, RevProd rev_prod, bool is_edge_query) const { int w = lca(u, v); T sml = e, smr = e; for (auto [l, r] : get_path(u, w, is_edge_query)) sml = op(sml, rev_prod(l, r)); for (auto [l, r] : get_path(v, w, true)) smr = op(prod(l, r), smr); return op(sml, smr); } pair get_subtree(int u, bool is_edge_query) const { return { visit[u] + is_edge_query, leave[u] }; } // vertex->id int get_id(int u) const { return visit[u]; } // id->vertexのvector vector inv_ids() const { vector inv(n); rep(i, 0, n) inv[visit[i]] = i; return inv; } vector roots() const { vector res; rep(i, 0, n) if (par[i] < 0) res.push_back(i); return res; } }; #undef CUT #line 3 "src/graph/BCC.hpp" #define CUT #line 2 "src/graph/LowLink.hpp" #line 4 "src/graph/LowLink.hpp" #define CUT struct LowLink { int n, m; vector> edges; vector>> g; vector ord, low, a_ids, b_ids; bool built = false; LowLink(const int n = 0): n(n), m(0), g(n), ord(n, -1), low(n) {} // Returns the id of the added edge int add_edge(int u, int v) { built = false; edges.emplace_back(u, v); g[u].emplace_back(v, m); g[v].emplace_back(u, m); return m++; } void build() { if (exchange(built, true)) return; int t = 0; auto dfs = [&](auto dfs, int u, int id) -> void { bool is_root = id < 0, is_art = false; int cnt = 0; ord[u] = low[u] = t++; for (auto [v, id2] : g[u]) if (id != id2) { if (ord[v] < 0) { ++cnt; dfs(dfs, v, id2); chmin(low[u], low[v]); if (ord[u] <= low[v]) { is_art = not is_root; if (ord[u] != low[v]) b_ids.push_back(id2); } } else { chmin(low[u], ord[v]); } } if (is_art or (is_root and cnt > 1)) { a_ids.push_back(u); } }; rep(i, 0, n) if (ord[i] < 0) dfs(dfs, i, -1); } // Returns `\{i \mid \text{$e_i$ is a bridge} \}` const vector& bridge_ids() { return build(), b_ids; } // Returns `\{i \mid \text{$v_i$ is an articulation point} \}` const vector& articulation_points() { return build(), a_ids; } bool is_bridge(int u, int v) { build(); if (ord[u] > ord[v]) swap(u, v); return ord[u] < low[v]; } }; #undef CUT #line 5 "src/graph/BCC.hpp" struct BCC { // isolated vertex <-> eid is empty vector vids, eids; }; struct BCCDecomp: LowLink { // cid[i]: the id of BCC to which `e_i` belongs vector cid; // isolated vertices vector iso; BCCDecomp(int n = 0): LowLink(n) {} int build() { if (built) return iso.size() + (m ? *max_element(all(cid)) + 1 : 0); LowLink::build(); vector used(n); int num = 0; cid.assign(m, -1); auto dfs = [&](auto dfs, int u, int peid) -> void { used[u] = true; static vector edges; for (const auto& [v, eid] : g[u]) if (eid != peid) { if (not used[v] or ord[v] < ord[u]) edges.push_back(eid); if (used[v]) continue; dfs(dfs, v, eid); if (low[v] < ord[u]) continue; int e; do cid[e = edges.back()] = num, edges.pop_back(); while (e != eid); num++; } }; for (int i = 0; i < n; ++i) if (not used[i]) { dfs(dfs, i, -1); if (g[i].empty()) iso.push_back(i); } return num + iso.size(); } vector bcc() { const int num = build(), vcmp = iso.size(), ecmp = num - vcmp; vector res(num); rep(i, 0, m) { res[cid[i]].eids.push_back(i); } vector seen(n, false); rep(id, 0, ecmp) { for (int eid : res[id].eids) { const auto& [u, v] = edges[eid]; if (not exchange(seen[u], true)) res[id].vids.push_back(u); if (not exchange(seen[v], true)) res[id].vids.push_back(v); } for (int v : res[id].vids) seen[v] = false; } rep(id, 0, vcmp) { res[ecmp + id].vids = { iso[id] }; } return res; } // Let `C := \text{set of BCC}` and `A := \text{set of articulation points}` // Vertex set of BCT `V := C\sqcup A` (`0 \leq i < |C|` corresponds to the `i`-th BCC and `|C| \leq i < |C| + |A|` corresponds to the `(i-|C|)`-th articulation point) // Edge set of BCT `E := \{(c, a) \in C\times A \mid a \in V(C) \}` vector> block_cut_tree(const vector &bccs) { const int cnum = bccs.size(), anum = a_ids.size(); vector ids(n, -1); rep(i, 0, anum) { ids[a_ids[i]] = cnum + i; } vector> res(cnum + anum); rep(i, 0, cnum) { for (int v : bccs[i].vids) { if (int j = ids[v]; j >= 0) { res[i].push_back(j); res[j].push_back(i); } } } return res; } // Let `C := \text{set of BCC}` and `A := \text{the vertex set of original graph}` // Vertex set of BCT `V := C\sqcup A` (`0 \leq i < |C|` corresponds to the `i`-th BCC and `|C| \leq i < |C| + |A|` corresponds to the `(i-|C|)`-th vertex) // Edge set of BCT `E := \{(c, a) \in C\times A \mid a \in V(C) \}` vector> ext_block_cut_tree(const vector &bccs) { const int cnum = bccs.size(); vector> res(cnum + n); rep(i, 0, cnum) { for (int v : bccs[i].vids) { res[i].push_back(cnum + v); res[cnum + v].push_back(i); } } return res; } }; #undef CUT #define NOTE 多重辺・自己ループがあってもよい #undef NOTE #line 5 "test/graph/bcc/yuki1326.test.cpp" int main() { int n, m; read(n, m); BCCDecomp bccd(n); rep(i, 0, m) { int u, v; read(u, v); --u, --v; bccd.add_edge(u, v); } auto bcc = bccd.bcc(); auto bct = bccd.ext_block_cut_tree(bcc); HLD hld(bct); const int k = bcc.size(); int q; read(q); rep(qid, 0, q) { int x, y; read(x, y); --x, --y; print(max(0, hld.dist(k + x, k + y) / 2 - 1)); } }