Files
python/TangDou/AcWing/DCC/397.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

114 lines
3.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}