You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 10, M = N << 2; //因为需要装无向图所以N=2e5,又因为有新图有旧图都保存到一个数组中所以需要再乘以2
//链式前向星
int e[M], h1[N], h2[N], idx, w[M], ne[M];
void add(int h[], int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int f[N][16];
int depth[N], dist[N];
void bfs() {
// 1号点是源点
depth[1] = 1;
queue<int> q;
q.push(1);
while (q.size()) {
int u = q.front();
q.pop();
for (int i = h2[u]; ~i; i = ne[i]) {
int j = e[i];
if (!depth[j]) {
q.push(j);
depth[j] = depth[u] + 1;
dist[j] = dist[u] + w[i];
f[j][0] = u;
for (int k = 1; k <= 15; k++) f[j][k] = f[f[j][k - 1]][k - 1];
}
}
}
}
//最近公共祖先
int lca(int a, int b) {
if (depth[a] < depth[b]) swap(a, b);
for (int k = 15; k >= 0; k--)
if (depth[f[a][k]] >= depth[b]) a = f[a][k];
if (a == b) return a;
for (int k = 15; k >= 0; k--)
if (f[a][k] != f[b][k]) a = f[a][k], b = f[b][k];
return f[a][0];
}
//边双连通分量
int dfn[N], low[N], ts, stk[N], top;
vector<int> dcc[N]; //边双中有哪些原始点
int id[N], dcc_cnt; //原始点x属于哪个边双连通分量dcc_cnt指边双连通分量个数
int is_bridge[M]; //记录哪些边是割边
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++ts;
stk[++top] = u;
for (int i = h1[u]; ~i; i = ne[i]) {
int j = e[i];
if (!dfn[j]) {
tarjan(j, i);
low[u] = min(low[u], low[j]);
if (dfn[u] < low[j]) is_bridge[i] = is_bridge[i ^ 1] = true; //记录割边
} else if (i != (fa ^ 1))
low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u]) {
++dcc_cnt; //边双数量+1
int x;
do {
x = stk[top--];
id[x] = dcc_cnt; // 记录点与边双关系
dcc[dcc_cnt].push_back(x); // 记录边双中有哪些点
} while (x != u);
}
}
int n, m, q;
int main() {
memset(h1, -1, sizeof h1); // h1是原图的表头
memset(h2, -1, sizeof h2); // h2是新生成的缩完点的图表头
scanf("%d %d", &n, &m);
//原图
for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d %d", &a, &b);
add(h1, a, b), add(h1, b, a);
}
//用tarjan来缩点将边双连通分量缩点
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i, -1);
//将新图建出来
for (int u = 1; u <= n; u++)
for (int i = h1[u]; ~i; i = ne[i]) {
int j = e[i];
if (id[u] != id[j])
add(h2, id[u], id[j]), add(h2, id[j], id[u]);
}
//随便找个存在的号作为根节点,预处理出每个点到根节点的距离
bfs();
scanf("%d", &q); // q次询问
for (int i = 1; i <= q; i++) {
int a, b;
scanf("%d %d", &a, &b);
a = id[a], b = id[b];
printf("%d\n", depth[a] + depth[b] - depth[lca(a, b)] * 2); //这就很显然了
}
return 0;
}