Tarjan專題總結複習
阿新 • • 發佈:2020-11-02
定義:
\(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;
}