心魔
塔揚縮點是我長時間不想學的算法了。。。並查集能解決的事絕對不用並查集!!!,然而,隨着題目難度加深,我發現有些題目不得不用Tarjan解決,而且現對於並查集而言,思維量可以大大減少,所以這里寫下這篇博客,算個紀念吧
Tarjan是一位非常操蛋帥的人,發明了了大量的算法,什么並查集求LCA啊,什么SPLAY啊...不過最出名的還是他發明的縮點算法了
正文
首先,什么叫縮點呢?我們需要先理解什么是強連通分量
強連通分量指的是:在一個有向圖中,強聯通分量的點可以互相到達,如在下圖中,一塊黃的就是一個強聯通分量
其實我不太說的清楚原理,直接上代碼注釋好了:
int DFN[maxn],LOW[maxn],index;//序號及環開頭的序號
int S[maxn],top;//手寫棧
bool ins[maxn];//是否進棧
int col[maxn],numc;//染色
void Tarjan(int u){
DFN[u] = LOW[u] = ++index;
S[++top] = u;//進棧
ins[u] = true;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(!DFN[v]){
Tarjan(v);
LOW[u] = min(LOW[u],LOW[v]);//找爸爸(環開頭)最小的
}
else if(ins[v]){
LOW[u] = min(LOW[u],DFN[v]);//判斷誰是爸爸
}
}
if(DFN[u] == LOW[u]){//發現更新完一輪自己是爸爸
numc++;
while(S[top + 1] != u){
col[S[top]] = numc;//出棧,染色
ins[S[top--]] = false;
}
}
}
關於Tarjan縮點的技巧
我們分兩種情況:
1.題目直接考Tarjan(重點)
這種情況一般是直接考縮點,通常和圖論的知識連用,而又以出度和入度最為常見
首先如果原圖是一個無向又環圖,我們是沒辦法對其進行某些操作的(比如可以重復走一條路但是點權只加一次這類的),因為要重復訪問,所以DFS就毫無用武之地了,這時候我們就需要又Tarjan,在跑Tarjan的時候處理環的某些性質(如點權什么的),在建新圖,就可以在新圖上進行dp或者遍歷了。
2.題目考其他
這里不再贅述,Tarjan就是一個輔助作用,把有環圖縮為無環圖,就可以使用一些算法解決問題了
最近刷的Tarjan的題: