1. 程式人生 > 實用技巧 >Tarjan專題總結複習

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]);
    }
}

割點/割邊

割點定義:若去掉無向聯通圖的某個點後,此圖不連通,則該點為割點。割邊同理。

判斷方法:

割邊:\(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;	
}