前言
题解
C. 洞穴探险
题目描述:
简单来说,就是 在一个无向图中,两个点之间关系 (存在多条简单路径,一条简单路径,不联通), 请判断两点之间的关系。
思路: 并查集 + tarjan割边
对于通联和非联通,其实很简单,只要简单的并查集即可。
但是仅有一条简单路径,多条简单路径的情况,就感觉很头痛。
所以呢,可以从删边的角度出发.
如果 A 到 B 存在一条简单路径,其任意一条边删去,都会导致 A B 不联通,那就是 O N E 关系,否则 M O R E 关系 如果A到B存在一条简单路径,其任意一条边删去,都会导致AB不联通,那就是ONE关系,否则MORE关系 如果A到B存在一条简单路径,其任意一条边删去,都会导致AB不联通,那就是ONE关系,否则MORE关系
而这样的删边,不就是割边吗?
因此引入2个并查集
- 用于维护图的联通性判定 S1
- 由割边图主导的联通性判定 S2
那么如果节点A,B属于S1
- A,B属于S2的同组,则A,B为ONE关系
- A,B不属于S2的同组,那么A,B存在多条简单路径,即为More
特别要注意:
该图是森林
该图是森林
该图是森林
#include <bits/stdc++.h>
using namespace std;
struct Dsu {
Dsu(int n) : n(n), arr(n, -1) {}
int find(int u) {
if (arr[u] == -1) {
return u;
}
return arr[u] = find(arr[u]);
}
void merge(int u, int v) {
int a = find(u), b = find(v);
if (a != b) {
arr[a] = b;
}
}
int n;
vector<int> arr;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t;
cin >> t;
while (t-- > 0) {
int n, m, q;
cin >> n >> m >> q;
vector<vector<int>> g(n);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
u--; v--;
g[u].push_back(v);
g[v].push_back(u);
}
Dsu dsu1(n);
Dsu dsu2(n);
int seqno = 0;
vector<bool> vis(n, false);
vector<int> dfn(n, 0), low(n, 0);
function<void(int, int)> tarjan;
tarjan = [&](int u, int fa) {
vis[u] = true;
dfn[u] = low[u] = ++seqno;
for (int v: g[u]) {
if (!vis[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) {
dsu2.merge(u, v);
}
} else if (v != fa) {
low[u] = min(low[u], dfn[v]);
}
dsu1.merge(u, v);
}
};
for (int i = 0; i < n; i++) {
if (!vis[i]) {
tarjan(i, -1);
}
}
while (q-- > 0) {
int u, v;
cin >> u >> v;
u--; v--;
if (dsu1.find(u) == dsu1.find(v) && dsu2.find(u) != dsu2.find(v)) {
cout << "MORE THAN ONE" << '\n';
} else if (dsu1.find(u) == dsu1.find(v)) {
cout << "ONE" << '\n';
} else {
cout << "NONE" << '\n';
}
}
}
return 0;
}