tarjan学习笔记

在 Tarjan 算法中为每个结点\(u\)维护了以下几个变量:
\(dfn[u]\):深度优先搜索遍历时结点\(u\)被搜索的次序。
\(low[u]\):设以\(u\)为根的子树为\(Subtree(u)\)。 \(low[u]\)定义为以下结点的\(dfn\)的最小值:

  • \(Subtree(u)\)中的结点;
  • \(Subtree(u)\)通过一条不在搜索树上的边能到达的结点。

如何计算\(low\)
首先让\(low[x]=dfn[x]\)(根据定义,最大也不过是\(dfn[x]\)
若在搜索树上\(x\)\(y\)的父节点,则\(low[x]=min(low[x],low[y])\) 递归求解
若无向边\((x,y)\)不是搜索树上的边,则\(low[x]=min(low[x],dfn[y])\)

性质:
一个结点的子树内结点的 \(dfn\) 都大于该结点的 \(dfn\)
从根开始的一条路径上的 \(dfn\) 严格递增,\(low\) 严格非降。

感性理解:\(low[x]\)就是从以\(x\)为根的搜索树子树内能到达的最小\(dfn\)

割边/割点

无向边\((x,y)\)是桥(割边),当且仅当搜索树上存在\(x\)的一个子节点\(y\),满足:\(dfn[x]<low[y]\)
感性理解:就是从\(y\)出发,一直在\(y\)的子树里转,因为\(y\)子树里的\(dfn\)都比\(x\)

\(x\)为割点,当且仅当存在一个点\(y\)\(dfn[x]<=low[y]\)
\(x\)为搜索树的根,那么要有两个点满足这个条件,因为每个点肯定都是会在这棵树里转的,如果有两个的话,两个各自在自己的子树里转就满足这个条件了(注意这是一颗dfs树,不是普通的树,每颗子树之间应该是不连通的)
感性理解:就是从\(y\)出发,一直在\(x\)的子树里转,因为\(x\)子树里的dfn都大于等于\(x\)

无向图的双连通分量

若一张无向连通图不存在割点,则称它为“点双连通图”(v-DCC)。
若一张无向连通图不存在割边,则称它为“边双连通图”(e-DCC)。
无向图的极大点双连通子图被称为“点双连通分量”
无向图的极大边双连通子图被称为“边双连通分量”

定理:一张无向连通图是“点双连通图”,当且仅当满足下列两个条件之一:
1.图的顶点数不超过2。
2.图中任意两点都同时包含在至少一个简单环中。其中“简单环‘指的是不自交的环。
定理: 一张无向连通图是“边双连通图”,当且仅当任意一条边都包含在至少一个简单环中。

感性理解即可

边双连通分量

只需求出无向图中所有桥,把桥删除后,就会分为若干个连通块,每个连通块就是一个“边双连通分量”。

有向图的缩点

如果一个点\(i\)的dfs做完后没有找到能回溯到的点,那么会有\(dfn[i]=low[i]\)\(i\)就是\(i\)所在强连通分量(树)的根节点。此时栈中的节点肯定满足这样几个条件,每个点的子树内都有指向\(dfn\)比他本身更小的节点,因为如果不是这样的话,那么在这里\(dfn[i]=low[i]\)就判定掉了。另外这些点都不会有指向前面子树的边,因为\(dfn[i]==low[i]\)。也不会有指向后面子树的边,因为这是一颗dfs树。所以这样就保证了每个点都在环上
注意,在代码中还有一个\(ins\),代表这个点是否在栈中。这样做的目的是如果指到了一个其他强连通分量的节点那显然不能构成环

点双连通分量(无向图的缩点)

和上面类似,但因为点双和强连通分量的定义有所区别,所以不同

此时如果\(low[to]\ge dfn[u]\)那么这就是一个割点,那么就将栈中的子树弹出,这颗子树里是不会有割点的,而\(u\)是割点,所以这就是极大的。而\(u\)还可能在其它点双中,不能弹出。
注意在代码中,我们没有\(ins\)因为这是求割点。然后弹的是\(to\)子树,上面弹的是\(u\)子树

缩点时是否需要instack标记取决于具体的图类型和算法需求。‌

首先求强连通分量是肯定要\(ins\)的,因为不在栈内的无法与其他构成一个强连通分量,而在求点双时因为是无向边所以可以构成。而求强连通分量时要保证恰好(点与点之间可以相互到达),所以是\(low[u]=dfn[u]\)。而点双连通分量是指在一个无向图中,‌去除任意一个顶点后,‌剩余的图仍然是连通的。‌所以是\(low[to]\ge dfn[u]\)
如果写等于的话会莫名其妙挂掉

哦,尼玛,我记得好像我之前有这个问题的。比如\(1-2\)这两个点的图,我们\(2\)\(low\)就是\(2\),但这个东西就是一个点双,所以有寄了,然后正确性就显然了

nm,这是无向图为什么

在有向图中进行缩点操作时,‌不需要记录\(pre\)(‌前驱节点)‌,‌但需要\(instack\)标记,‌这是为了过滤“横边”,‌即那些指向当前节点但不是从已访问节点指向当前节点的边。‌这种标记帮助识别和排除那些不构成强连通分量的边,‌确保算法正确性。‌

然而,‌在无向图中进行缩点操作时,‌需要记录\(pre\)以避免回边(‌back edge)‌,‌即节点之间的循环依赖。‌有时还需要考虑重边的情况。‌在这种情况下,‌不需要\(instack\)标记,‌因为无向图的边没有方向性,‌不需要特别标记来处理“横边”问题。‌

对于点双连通分量的求法,‌不需要\(instack\)标记。‌这是因为点双连通分量的算法在处理无向图时,‌通过记录\(pre\)和避免回边来确保算法的正确性,‌而不需要额外的\(instack\)标记来处理“横边”。‌

综上所述,‌是否需要\(instack\)标记取决于图的类型(‌有向图或无向图)‌以及具体的算法需求。‌在有向图的缩点算法中,\(‌instack\)标记用于过滤“横边”,‌确保算法正确性;‌而在无向图的点双连通分量求法中,‌由于通过记录\(pre\)和避免回边来处理边和节点,‌因此不需要\(instack\)标记。‌

好多tarjan题:

P2860
意思是没有割边,即让所有边都在一个环上。可以想到先无向图按照割边缩点,形成了一棵树,然后两两连叶子,具体证明我也不会,然后连的方式大概是这样大概就是每次连边都会消掉边上的哪些链,然后就可以了。

CF1000E
必须经过边就是割边了,那么无向图按照割边缩点,形成了一棵树,然后树的直径就是答案

posted @ 2024-04-21 20:40  wuhupai  阅读(37)  评论(0)    收藏  举报