割边
代码模板
cpp
copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 与求割点不同,求割边时有两个关键点需要注意:
// 1.判定条件更严格:low[v] > dfn[u] (不能取等号)。
// 因为如果 low[v] == dfn[u],说明 $v$ 还能回到 $u$,那么 $u-v$ 这条边在环上,
// 不是桥。防止走回头路:在无向图中,判定“不走回头路”时,
// 2.不能简单地判断 v != father,因为两个点之间可能存在重边(多条边)。
// 我们需要通过边的编号来判断——即“不要走刚才进来的那条边的反向边”。
struct TarjanBridge {
int n, timer;
int dfn[maxn], low[maxn];
bool is_bridge[maxe * 2]; // 标记边是否为桥 (注意大小是边数)
void set(int _n) {
n = _n;
timer = 0;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(is_bridge, 0, sizeof(is_bridge));
}
/*
* u: 当前节点
* in_edge: 进入 u 点的那条边的编号 (替代 father 参数)
* 用于处理重边情况,确保只屏蔽刚才过来的那一条具体的边
*/
void dfs(int u, int in_edge) {
dfn[u] = low[u] = ++timer;
// 遍历以 u 为起点的所有边
for (int i = e.h[u]; i != -1; i = e[i].next) {
int v = e[i].v;
// 判定条件:i 不是“进入 u 的那条边”的反向边
// in_edge 是进入 u 的边,in_edge ^ 1 是从 u 回去父亲的边
// 过滤掉刚才过来的那条边的反向边
if( i == (in_edge ^ 1) ) continue;
if (!dfn[v]) { // 树枝边
dfs(v, i); // 将当前边 i 传入下一层
low[u] = std::min(low[u], low[v]);
// 核心:割边判定
// 必须是 >,不能是 >=。
// 如果 low[v] > dfn[u],说明 v 及其子树无法回到 u 或 u 的祖先
//
// 这里的 else if (dfn[v] < dfn[u]) 其实是个很好的防御性编程。
// 在无向图 DFS 树中,如果 v 已访问且不是父亲,它一定是祖先 (dfn[v] < dfn[u])。
// 唯一的例外是有重边连接到由 u 刚访问过的子节点(此时 dfn[v] > dfn[u]),
// 但这种情况不需要更新 low[u],因为子节点的 low 已经处理过这种情况了。
if (low[v] > dfn[u]) {
is_bridge[i] = is_bridge[i ^ 1] = true;
}
}
else if( dfn[v] < dfn[u] ) { // 回边
low[u] = std::min(low[u], dfn[v]);
}
}
}
void solve() {
// 遍历所有连通分量
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
// 对于根节点,没有“进入的边”,传入 -1
// -1 ^ 1 结果是一个很大的负数或特定值,肯定不会等于正常的边索引(>=0)
dfs(i, -1);
}
}
}
// 辅助:获取所有桥 (以 u < v 形式返回)
std::vector<std::pair<int, int>> get_bridges() {
std::vector<std::pair<int, int>> ans;
// edge_cnt 是总边数索引,遍历 0 到 edge_cnt-1
// 每次 += 2 跳过反向边,避免重复添加
for (int i = 0; i < edge_cnt; i += 2) {
if (is_bridge[i]) {
ans.push_back({e[i ^ 1].v, e[i].v}); // u=e[i^1].v, v=e[i].v
}
}
return ans;
}
};