#include #include #include #include #include #include namespace atcoder { namespace internal { #ifndef _MSC_VER template using is_signed_int128 = typename std::conditional::value || std::is_same::value, std::true_type, std::false_type>::type; template using is_unsigned_int128 = typename std::conditional::value || std::is_same::value, std::true_type, std::false_type>::type; template using make_unsigned_int128 = typename std::conditional::value, __uint128_t, unsigned __int128>; template using is_integral = typename std::conditional::value || is_signed_int128::value || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using is_signed_int = typename std::conditional<(is_integral::value && std::is_signed::value) || is_signed_int128::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional<(is_integral::value && std::is_unsigned::value) || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional< is_signed_int128::value, make_unsigned_int128, typename std::conditional::value, std::make_unsigned, std::common_type>::type>::type; #else template using is_integral = typename std::is_integral; template using is_signed_int = typename std::conditional::value && std::is_signed::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional::value && std::is_unsigned::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional::value, std::make_unsigned, std::common_type>::type; #endif template using is_signed_int_t = std::enable_if_t::value>; template using is_unsigned_int_t = std::enable_if_t::value>; template using to_unsigned_t = typename to_unsigned::type; } // namespace internal } // namespace atcoder namespace atcoder { template struct fenwick_tree { using U = internal::to_unsigned_t; public: fenwick_tree() : _n(0) {} fenwick_tree(int n) : _n(n), data(n) {} void add(int p, T x) { assert(0 <= p && p < _n); p++; while (p <= _n) { data[p - 1] += U(x); p += p & -p; } } T sum(int l, int r) { assert(0 <= l && l <= r && r <= _n); return sum(r) - sum(l); } private: int _n; std::vector data; U sum(int r) { U s = 0; while (r > 0) { s += data[r - 1]; r -= r & -r; } return s; } }; } // namespace atcoder using namespace std; using namespace atcoder; #define rep(i,n) for(int i = 0; i < (n); ++i) #define rrep(i,n) for(int i = (n)-1; i >= 0; --i) #define chmax(a, b) a = max(a, b) #define chmin(a, b) a = min(a, b) #define all(x) (x).begin(), (x).end() #define rall(x) (x).rbegin(), (x).rend() using ll = long long; using P = pair; using VI = vector; using VVI = vector; using VL = vector; using VVL = vector; struct HLD { std::vector> &to; int root, n; std::vector sz, parent, depth, idx, ridx, head, inv; HLD(std::vector>& to, int root=0): to(to), root(root), n(to.size()), sz(n), parent(n), depth(n), idx(n), ridx(n), head(n), inv(n) { init_tree_data(root); int x = 0; assign_idx(root, root, x); } void init_tree_data(int u, int p=-1, int d=0) { parent[u] = p; depth[u] = d; int s = 1; for(int v: to[u]) { if (v == p) continue; init_tree_data(v, u, d+1); s += sz[v]; } sz[u] = s; } void assign_idx(int u, int h, int &nxt, int p=-1) { head[u] = h; inv[nxt] = u; idx[u] = nxt++; if (sz[u] == 1) { ridx[u] = nxt; return; } int mxsize = 0; int mi; for(int v: to[u]) { if (v == p) continue; if (sz[v] > mxsize) { mxsize = sz[v]; mi = v; } } assign_idx(mi, h, nxt, u); for(int v: to[u]) { if (v == p || v == mi) continue; assign_idx(v, v, nxt, u); } ridx[u] = nxt; } int lca(int u, int v) { while(head[u] != head[v]) { if (depth[head[u]] > depth[head[v]]) u = parent[head[u]]; else v = parent[head[v]]; } return (depth[u] < depth[v] ? u : v); } // returns (paths upto lca from x (excluding lca), those from y, lca) std::tuple>, std::vector>, int> paths(int x, int y) { std::tuple>, std::vector>, int> ret; std::vector>& x_paths = get<0>(ret); std::vector>& y_paths = get<1>(ret); int& lca = get<2>(ret); while(head[x] != head[y]) { int xhead = head[x], yhead = head[y]; if (depth[xhead] > depth[yhead]) { x_paths.emplace_back(x, xhead); x = parent[xhead]; } else { y_paths.emplace_back(y, yhead); y = parent[yhead]; } } if (depth[x] > depth[y]) { int ychild = inv[idx[y] + 1]; x_paths.emplace_back(x, ychild); x = y; } else if (depth[x] < depth[y]) { int xchild = inv[idx[x] + 1]; y_paths.emplace_back(y, xchild); y = x; } lca = x; return ret; } }; std::tuple< std::vector>, std::vector, std::vector> lowlink(std::vector>& to, int root=0) { // returns a tuple of (spanning_tree (directed), ord, low) std::tuple< std::vector>, std::vector, std::vector> res; int n = to.size(); auto& tree = std::get<0>(res); auto& ord = std::get<1>(res); auto& low = std::get<2>(res); tree.resize(n); ord.resize(n, -1); low.resize(n); int nxt_ord = 0; auto dfs = [&](auto&& self, int u, int parent=-1)->void { low[u] = ord[u] = nxt_ord++; for(int v: to[u]) { if (v == parent) continue; if (ord[v] == -1) { tree[u].push_back(v); self(self, v, u); if (low[v] < low[u]) low[u] = low[v]; } else { if (ord[v] < low[u]) low[u] = ord[v]; } } }; dfs(dfs, root); return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n, m; cin >> n >> m; VVI graph(n); rep(i, m) { int u, v; cin >> u >> v; --u, --v; graph[u].push_back(v); graph[v].push_back(u); } auto [tree, ord, low] = lowlink(graph); HLD d(tree); fenwick_tree fw(n); for(int i = 1; i < n; i++) { int p = d.parent[i]; if (p == 0 && tree[p].size() >= 2 || p != 0 && low[i] >= ord[p]) fw.add(d.idx[i], 1); } int q; cin >> q; while(q--) { int x, y; cin >> x >> y; x--, y--; if (x == y) { cout << 0 << '\n'; continue; } if (d.depth[x] > d.depth[y]) swap(x, y); auto [xpaths, ypaths, lca] = d.paths(x, y); int ans = 0; bool x_added = false, y_added = false; for(auto [s, t]: xpaths) { int si = d.idx[s], ti = d.idx[t]; ans += fw.sum(ti, si + 1); x_added = fw.sum(ti, ti + 1) == 1; } for(auto [s, t]: ypaths) { int si = d.idx[s], ti = d.idx[t]; ans += fw.sum(ti, si + 1); y_added = fw.sum(ti, ti + 1) == 1; } if ((x == lca || x_added) && y_added) ans -= 1; cout << ans << '\n'; } }