#define _USE_MATH_DEFINES #include using namespace std; #define FOR(i,m,n) for(int i=(m);i<(n);++i) #define REP(i,n) FOR(i,0,n) #define ALL(v) (v).begin(),(v).end() using ll = long long; constexpr int INF = 0x3f3f3f3f; constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL; constexpr double EPS = 1e-8; constexpr int MOD = 1000000007; // constexpr int MOD = 998244353; constexpr int dy[] = {1, 0, -1, 0}, dx[] = {0, -1, 0, 1}; constexpr int dy8[] = {1, 1, 0, -1, -1, -1, 0, 1}, dx8[] = {0, -1, -1, -1, 0, 1, 1, 1}; template inline bool chmax(T &a, U b) { return a < b ? (a = b, true) : false; } template inline bool chmin(T &a, U b) { return a > b ? (a = b, true) : false; } struct IOSetup { IOSetup() { std::cin.tie(nullptr); std::ios_base::sync_with_stdio(false); std::cout << fixed << setprecision(20); } } iosetup; struct Lowlink { std::vector> graph; std::vector ap; std::vector> bridge; Lowlink(const std::vector> &graph) : graph(graph) { int n = graph.size(); order.assign(n, -1); lowlink.resize(n); int tm = 0; for (int i = 0; i < n; ++i) { if (order[i] == -1) dfs(-1, i, tm); } // std::sort(ap.begin(), ap.end()); // std::sort(bridge.begin(), bridge.end(), [](const pair &a, const pair &b) -> bool { // int as, ad, bs, bd; // std::tie(as, ad) = a; // std::tie(bs, bd) = b; // return as != bs ? as < bs : ad < bd; // }); } std::vector order, lowlink; private: void dfs(int par, int ver, int &tm) { order[ver] = lowlink[ver] = tm++; int cnt = 0; bool is_ap = false; for (int e : graph[ver]) { if (order[e] == -1) { ++cnt; dfs(ver, e, tm); if (lowlink[e] < lowlink[ver]) lowlink[ver] = lowlink[e]; if (order[ver] <= lowlink[e]) { is_ap = true; if (order[ver] < lowlink[e]) bridge.emplace_back(std::minmax(ver, e)); } } else if (e != par) { if (order[e] < lowlink[ver]) lowlink[ver] = order[e]; } } if (par == -1) { if (cnt >= 2) ap.emplace_back(ver); } else { if (is_ap) ap.emplace_back(ver); } } }; struct BiconnectedComponent : Lowlink { std::vector>> block; std::vector id; std::vector> vertices, cutpoint; BiconnectedComponent(const std::vector> &graph, bool heavy = false) : Lowlink(graph), heavy(heavy) { int n = graph.size(); id.assign(n, -2); if (heavy) { cutpoint.resize(n); is_ap.assign(n, false); for (int e : this->ap) is_ap[e] = true; } for (int i = 0; i < n; ++i) { if (id[i] == -2) { id[i] = -1; dfs(-1, i); } } // if (heavy) { // int sz = vertices.size(); // for (int i = 0; i < sz; ++i) { // std::sort(block[i].begin(), block[i].end()); // std::sort(vertices[i].begin(), vertices[i].end()); // } // for (int i = 0; i < n; ++i) std::sort(cutpoint[i].begin(), cutpoint[i].end()); // } } private: using P = std::pair; bool heavy; std::vector is_ap; std::vector

tmp; void dfs(int par, int ver) { for (int e : this->graph[ver]) { if (e == par) continue; if (id[e] == -2 || this->order[ver] > this->order[e]) tmp.emplace_back(std::minmax(ver, e)); if (id[e] == -2) { id[e] = -1; dfs(ver, e); if (this->order[ver] <= this->lowlink[e]) { int idx = block.size(); block.emplace_back(); std::set st; while (true) { P pr = tmp.back(); block[idx].emplace_back(pr); if (heavy) { st.emplace(pr.first); st.emplace(pr.second); } tmp.pop_back(); if (pr.first == std::min(ver, e) && pr.second == std::max(ver, e)) break; } if (heavy) { vertices.emplace_back(); for (int el : st) { vertices[idx].emplace_back(el); if (is_ap[el]) { cutpoint[el].emplace_back(idx); } else { id[el] = idx; } } } } } } } }; struct HLD { std::vector parent, subtree, id, inv, head; HLD(const std::vector> &graph, int root = 0) : graph(graph) { int n = graph.size(); parent.assign(n, -1); subtree.assign(n, 1); id.resize(n); inv.resize(n); head.resize(n); dfs1(root); head[root] = root; int now_id = 0; dfs2(root, now_id); } template void v_update(int u, int v, Fn f) const { while (true) { if (id[u] > id[v]) std::swap(u, v); f(std::max(id[head[v]], id[u]), id[v] + 1); if (head[u] == head[v]) return; v = parent[head[v]]; } } template T v_query(int u, int v, F f, G g, const T UNITY) const { T left = UNITY, right = UNITY; while (true) { if (id[u] > id[v]) { std::swap(u, v); std::swap(left, right); } left = g(left, f(std::max(id[head[v]], id[u]), id[v] + 1)); if (head[u] == head[v]) break; v = parent[head[v]]; } return g(left, right); } template void subtree_v_update(int ver, Fn f) const { f(id[ver], id[ver] + subtree[ver]); } template T subtree_v_query(int ver, Fn f) const { return f(id[ver], id[ver] + subtree[ver]); } template void e_update(int u, int v, Fn f) const { while (true) { if (id[u] > id[v]) std::swap(u, v); if (head[u] == head[v]) { f(id[u], id[v]); break; } else { f(id[head[v]] - 1, id[v]); v = parent[head[v]]; } } } template T e_query(int u, int v, F f, G g, const T UNITY) const { T left = UNITY, right = UNITY; while (true) { if (id[u] > id[v]) { std::swap(u, v); std::swap(left, right); } if (head[u] == head[v]) { left = g(left, f(id[u], id[v])); break; } else { left = g(left, f(id[head[v]] - 1, id[v])); v = parent[head[v]]; } } return g(left, right); } template void subtree_e_update(int ver, Fn f) const { f(id[ver], id[ver] + subtree[ver] - 1); } template T subtree_e_query(int ver, Fn f) const { return f(id[ver], id[ver] + subtree[ver] - 1); } int lca(int u, int v) const { while (true) { if (id[u] > id[v]) std::swap(u, v); if (head[u] == head[v]) return u; v = parent[head[v]]; } } private: std::vector> graph; void dfs1(int ver) { for (int &e : graph[ver]) { if (e != parent[ver]) { parent[e] = ver; dfs1(e); subtree[ver] += subtree[e]; if (subtree[e] > subtree[graph[ver].front()]) std::swap(e, graph[ver].front()); } } } void dfs2(int ver, int &now_id) { id[ver] = now_id++; inv[id[ver]] = ver; for (int e : graph[ver]) { if (e != parent[ver]) { head[e] = (e == graph[ver].front() ? head[ver] : e); dfs2(e, now_id); } } } }; template struct DisjointSparseTable { DisjointSparseTable(const std::vector &a, Fn fn) : fn(fn) { int n = a.size(), table_h = 1; while ((1 << table_h) < n) ++table_h; lg.assign(1 << table_h, 0); for (int i = 2; i < (1 << table_h); ++i) lg[i] = lg[i >> 1] + 1; dat.assign(table_h, std::vector(n)); for (int j = 0; j < n; ++j) dat[0][j] = a[j]; for (int i = 1; i < table_h; ++i) { int shift = 1 << i; for (int left = 0; left < n; left += shift << 1) { int mid = std::min(left + shift, n); dat[i][mid - 1] = a[mid - 1]; for (int j = mid - 2; j >= left; --j) dat[i][j] = fn(a[j], dat[i][j + 1]); if (n <= mid) break; int right = std::min(mid + shift, n); dat[i][mid] = a[mid]; for (int j = mid + 1; j < right; ++j) dat[i][j] = fn(dat[i][j - 1], a[j]); } } } Semigroup query(int left, int right) const { assert(left < right); if (left == --right) return dat[0][left]; int h = lg[left ^ right]; return fn(dat[h][left], dat[h][right]); } private: Fn fn; std::vector lg; std::vector> dat; }; int main() { int n, m; cin >> n >> m; vector> graph(n); while (m--) { int u, v; cin >> u >> v; --u; --v; graph[u].emplace_back(v); graph[v].emplace_back(u); } BiconnectedComponent bc(graph, true); int b = bc.block.size(), a = bc.ap.size(); vector> bctree(b + a); REP(i, a) for (int e : bc.cutpoint[bc.ap[i]]) { bctree[bc.block.size() + i].emplace_back(e); bctree[e].emplace_back(bc.block.size() + i); } vector rev(n, -1); REP(i, a) rev[bc.ap[i]] = i; HLD hld(bctree); vector w(b + a, 0); FOR(i, b, b + a) w[hld.id[i]] = 1; DisjointSparseTable dst(w, [](int a, int b) -> int { return a + b; }); int q; cin >> q; while (q--) { int x, y; cin >> x >> y; --x; --y; if (x == y) { cout << 0 << '\n'; continue; } x = rev[x] == -1 ? bc.id[x] : b + rev[x]; y = rev[y] == -1 ? bc.id[y] : b + rev[y]; int ans = hld.v_query(x, y, [&](int l, int r) { return dst.query(l, r); }, [](int a, int b) { return a + b; }, 0); ans -= (x >= b) + (y >= b); cout << ans << '\n'; } return 0; }