古典叙事 · 技术札记

序章

山河有卷
人间有声

写代码,也写长风、旧城与未熄的灯。
愿每一篇随笔,都有自己的山水与回声。
阅览随笔

Tarjan算法

连通性相关有:强连通分量,双连通分量,割点和桥,边,点连通分量。

强连通分量

强连通的定义是:有向图 G 强连通是指,G 中任意两个结点连通。
强连通分量(Strongly Connected Components,SCC)的定义是:极大的强连通子图(就是这个子图中的任意两个点可以互相到达)

Tarjan算法

Tarjan 算法基于对图进行 深度优先搜索。我们视每个连通分量为搜索树中的一棵子树,在搜索过程中,维护一个栈,每次把搜索树中尚未处理的节点加入栈中。
用low数组储存的子树中能够回溯到的最早的已经在栈中的结点,dfn储存这个节点的dfs序列
看一下这道题:

模板题

B 国有 n 个城市,这些城市以铁路相连。任意两个城市都可以通过铁路直接或者间接到达,希望炸毁铁路,以达到存在某两个城市无法互相通过铁路到达的效果。
然而,只有一发炮弹(A 国国会不给钱了)。所以,他能轰炸哪一条铁路呢?
所以我们要找出所有的如果删除这条边就会让图中有些点不连通的边
https://www.luogu.com.cn/problem/P1656
我们用tarjan算法完成这道题
用low存这个点能通过它的子孙到达的dfn值最小的点,dfn存深度优先搜索时第几个遍历,所以当我的儿子节点的low都比我大的时候,说明我和我的儿子节点只有一条路径可以到达

void dfs(i64 now , i64 fa) {
/*dfn:
	{
		下标:点编号
			内存的值:深度优先搜索时第几个遍历
	}
low:
	{
		下标:点编号
			内存的值:这个点能通过它的子孙到达的dfn值最小的点的dfn
	}*/
	dfn [ now ] = ++cnt;//记录节点遍历顺序
	low [ now ] = dfn [ now ];//更新节点的low数组
	for(auto it:t [ now ] ) {
		if (dfn [ it ] && it != fa) {//如果子节点已经被更新,那么父节点可以到达所有子节点的位置
			low [ now ] = min(low [ now ] , dfn [ it ]);//更新
		}
		if (!dfn [ it ]) {//没有遍历过
			dfs(it , now);
			if (dfn [ now ] < low [ it ]) {//如果当前节点的遍历顺序,小于子节点子树中的最早的点
				edg.push_back({ min(now,it) , max(it,now)});
				
			}
			low [ now ] = min(low [ now ] , low [ it ]);//更新
		}
	}
}
AC代码
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
using i64 = long long;
vector<pair<i64 , i64>>edg;
vector<i64> t [ 300005 ];
i64 n,m;
i64 cnt=0,low[300005],dfn[300005];//cnt代表时间戳,low代表
bool cmp(pair<i64 , i64> a , pair<i64 , i64> b) {
	if (a.first == b.first)
		return a.second < b.second;
	return a.first < b.first;
}
void dfs(i64 now , i64 fa) {
/*dfn:
	{
		下标:点编号
			内存的值:深度优先搜索时第几个遍历
	}
low:
	{
		下标:点编号
			内存的值:这个点能通过它的子孙到达的dfn值最小的点的dfn
	}*/
	dfn [ now ] = ++cnt;//记录节点遍历顺序
	low [ now ] = dfn [ now ];//更新节点的low数组
	for(auto it:t [ now ] ) {
		if (dfn [ it ] && it != fa) {//如果子节点已经被更新,那么父节点可以到达所有子节点的位置
			low [ now ] = min(low [ now ] , dfn [ it ]);//更新
		}
		if (!dfn [ it ]) {//没有遍历过
			dfs(it , now);
			if (dfn [ now ] < low [ it ]) {//如果当前节点的遍历顺序,小于子节点子树中的最早的点
				edg.push_back({ min(now,it) , max(it,now)});
				
			}
			low [ now ] = min(low [ now ] , low [ it ]);//更新
		}
	}
}
int main() {
	cin >> n>>m;
	for (int i = 1; i <= m; i++) {
		i64 x , y;
		cin >> x >> y;
		t [ x ].push_back(y);
		t [ y ].push_back(x);
	}
	for (int i = 1; i <= n; i++) {
		if (!dfn [ i ])
			dfs(i , i);
	}
	sort(edg.begin() , edg.end() , cmp);
	for (auto it : edg) {
		cout << it.first << " " << it.second << endl;
	}
}
posted @ 2025-08-19 11:33  Morphis‘  阅读(29)  评论(0)    收藏  举报

特别策划 · CINEMATIC NOTES

风沙与孤骑

风起塞外,
胜负在刀剑之前。

“把复杂拆成秩序,把未知写成答案。”
Morphis · 山河一卷 愿你从这里出发,仍能听见山风。 影像:farfarSébastien Goldberg / Unsplash