题目链接
思路
在一张无向连通图图上,将所有的环全都缩成一个点,我们可以使用Tarjan边双连通分量进行割边缩点。
缩点之后的图一定是一棵树。
在这棵树上,我们想要快速求出 a a a和 b b b两个点之间的节点个数,可以先预处理出这棵树的LCA。
利用每一个节点的深度进行快速求解,公式为:
d e p t h [ a ] + d e p t h [ b ] − d e p t h [ l c a ] ∗ 2 + 1 depth[a] + depth[b] - depth[lca]*2 + 1 depth[a]+depth[b]−depth[lca]∗2+1。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 5, M = 1e5 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m, tot;
int u[M], v[M];
vector<pair<int, int>>edge;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], tim;
int id[N], bcc_cnt;
stack<int> st;
bool is_bridge[M];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void tarjan(int u, int fu)
{
dfn[u] = low[u] = ++tim;
st.push(u);
for (int i = h[u]; i != -1; 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 != (fu ^ 1))
{
low[u] = min(low[u], dfn[j]);
}
}
if (dfn[u] == low[u])
{
++bcc_cnt;
int k;
do
{
k = st.top();
st.pop();
id[k] = bcc_cnt;
} while (k != u);
}
}
struct DSU {
std::vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
struct LCA
{
vector<vector<int>>mp;
vector<int>depth;
vector<vector<int>>fa;
LCA() {}
LCA(int n) {init(n);}
void init(int n)
{
mp.resize(n + 1);
depth.resize(n + 1);
fa.resize(n + 1, vector<int>(20));
}
void add_edge(int a, int b)
{
//建双向边
mp[a].push_back(b);
mp[b].push_back(a);
}
void bfs(int root)
{
fill(depth.begin(), depth.end(), inf);
depth[0] = 0, depth[root] = 1;
queue<int>q;
q.push(root);
while (q.size())
{
int u = q.front();
q.pop();
for (int i = 0; i < mp[u].size(); i++)
{
int j = mp[u][i];
if (depth[j] > depth[u] + 1)
{
depth[j] = depth[u] + 1;
q.push(j);
fa[j][0] = u;
for (int k = 1; k <= 19; k++)
{
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
int lca(int a, int b)
{
if (depth[a] < depth[b]) swap(a, b);
for (int k = 19; k >= 0; k -- )
if (depth[fa[a][k]] >= depth[b])
a = fa[a][k];
if (a == b) return a;
for (int k = 19; k >= 0; k -- )
if (fa[a][k] != fa[b][k])
{
a = fa[a][k];
b = fa[b][k];
}
return fa[a][0];
}
};
void solve()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i++)
{
cin >> u[i] >> v[i];
if (u[i] == v[i]) continue;
edge.push_back({min(u[i], v[i]), max(u[i], v[i])});
}
sort(edge.begin(), edge.end());
int high = unique(edge.begin(), edge.end()) - edge.begin();
for (int i = 0; i < high; i++)
{
add(edge[i].first, edge[i].second);
add(edge[i].second, edge[i].first);
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
tarjan(i, 0);
}
DSU dsu(bcc_cnt + 1);
LCA tree(bcc_cnt);//tarjan边双连通分量之后的图一定是一棵树
for (int i = 0; i < high; i++)
{
int bcc_u = id[edge[i].first], bcc_v = id[edge[i].second];
if (bcc_u != bcc_v && !dsu.same(bcc_u, bcc_v))
{
tree.add_edge(bcc_u, bcc_v);
dsu.merge(bcc_u, bcc_v);
}
}
tree.bfs(1);
cin >> tot;
while (tot--)
{
int a, b;
cin >> a >> b;
a = id[a];
b = id[b];
int zu = tree.lca(a, b);
int ans = tree.depth[a] + tree.depth[b] - tree.depth[zu] * 2 + 1;
vector<int>bit;
while (ans)
{
bit.push_back(ans % 2);
ans /= 2;
}
reverse(bit.begin(), bit.end());
for (int val : bit)
{
cout << val;
}
cout << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve();
}
return 0;
}