天天看點

LCA離線算法Tarjan詳解

離線算法也就是需要先把所有查詢給儲存下來,最後一次輸出結果。

離線算法是基于并查集實作的,首先就是初始化P[i] = i。

接下來對于每個點進行dfs:

①首先判斷是否有與該點有關的查詢,如果目前該點為u,與它有關的點為v,如果v已經通路過了,那麼它們的LCA就是find(v)。如果v還沒有通路,那就不用管它。

②對該點的子節點繼續dfs,需要注意的是,dfs完之後需要需要p[v]=u,将v點并到其父親節點上。

1 void LCA(int u)
 2 {
 3     vis[u]=1;
 4     for(int i=qhead[u];i!=-1;i=query[i].next)
 5     {
 6         int v=query[i].v;
 7         if(vis[v] && !mark[Find(v)])  //mark數組是為了針對非連通圖的情況
 8             ans[query[i].index]=Find(v);  //index次詢問的公共祖先為Find(v)
 9     }
10 
11     for(int i=ehead[u];i!=-1;i=e[i].next)
12     {
13         int v=e[i].v;
14         if(!vis[v])
15         {17             LCA(v);
18             p[v]=u;
19         }
20     }
21 }      

接下來詳細解釋一下為什麼是這麼一個原理:

對于任意兩個節點u和v來說,它們隻有兩種關系:①子節點關系;②非子節點關系。

①子節點關系

LCA離線算法Tarjan詳解

u和v的公共祖先很明顯的就是u,在通路u結點的時候,v節點還沒有被通路,此時繼續通路u的子節點。那麼當通路到v節點的時候,因為u節點已經被通路,是以此時u、v的LCA=find(u),由于此時p[u]=u,是以此時它們的LCA就是u。

②非子節點關系

LCA離線算法Tarjan詳解

此時u和v兩個節點肯定有一個先通路,另一個後通路,現在就假設u先通路(v先通路的情況也是一樣的)。

通路到u時,v還沒有被通路,是以此時不用管,繼續通路u的子節點,當u的子節點通路完之後,p[u] = f。