Tarjan专题总结复习

定义:

\(dfn[x]\)\(x\)第一次被访问的时间顺序(时间戳)

搜索树:每个节点只访问一次,所有访问过的边\((x,y)\)构成一棵搜索树

\(low[x]\):定义为以下节点的时间戳的最小值:

\(1.\)\(subtree(x)\)中的节点。

\(2.\)通过\(1\)不在搜索树上的边,能够到达\(subtree(x)\)的节点。

代码:(有向图强连通分量)

void Tarjan(int x)
{
    low[x] = dfn[x] = ++ ind;//初始值
    que[ ++ top] = x;//进队
    vis[x] = 1;//标记
    for (int i = hd[x]; i; i = nxt[i])
    {
	int y = to[i];//枚举x子节点y
	if (!dfn[y])
	{
	    Tarjan(y);
	    low[x] = min(low[x], low[y]);//在子树中,是x的儿子,直接更新
	}
	else if (vis[y]) low[x] = min(low[x], dfn[y]);
        //已经访问过,且y不是x的儿子,因为dfn[y]一定小于dfn[x],那么用dfn[y]更新(不用low[y]更新,因为它们不一定在同一个强连通分量中,防止更新过头)
    }
    if (dfn[x] == low[x])//形成了一个环,说明x是一个强连通分量的根
    {
        cnt ++ ;//新的强连通分量
        int now = -1;
        do
        {
            now = que[top -- ];
            vis[now] = 0;
            col[now] = cnt;//染色,标记now属于当前强连通分量cnt
        } while (now != x);//弹出
    }
    return;
}

应用

缩点

在跑了一遍Tarjan后,对于原来的每条边对应的点对\((x,y)\),若\(col[x]==col[y]\),说明它们在同一个强连通分量内,则它们不用在新的图中连边,否则要连。

伪代码:

for (int x = 1; x <= n; ++ x)
{
    for (int i = hd[x]; i; i = nxt[i])
    {
        int y = to[i];
        if (col[x] != col[y]) add(col[x], col[y]);
    }
}

割点/割边

注意:无向图中不要记\(vis\),只用传一条边进去即可,但是此时一个点也会被判为强连通分量

割点定义:若去掉无向联通图的某个点后,此图不连通,则该点为割点。割边同理。

判断方法:

割边:\(dfn[x]<low[y]\)(说明从\(subtree(y)\)出发,在不经过(x,y)的前提下,无论走那条边,都无法到达\(x\)或比\(x\)更早的节点,这就是一条割边)

割点:\(dfn[x]\le{low[y]}\)(和割边同理。特别地,若\(x\)是搜索树根节点,那么\(x\)是割点当且仅当它存在至少两个子节点\(y_1,y_2\)满足条件)

伪代码(割点)

if (low[y] >= dfn[x])
{
    t ++ ;		
    if (x != rt || t > 1) cut[x] = 1;	
}

点双/边双

https://www.cnblogs.com/sdfzjdx/p/10503486.html

posted @ 2020-11-02 13:18  andysj  阅读(74)  评论(0编辑  收藏  举报