LCA離線演算法學習筆記

2021-07-30 09:42:00 字數 1552 閱讀 1191

lca離線,採用的是遞迴的tarjan演算法,利用了樹深度優先遍歷的性質可以在一次遍歷過程中巧妙的求解出查詢的最接近公共祖先,時間複雜度是o(n+q),但前提是需要離線儲存所有詢問。

演算法思路如下:

當前dfs遍歷到節點u,先將vis[u]標記成true,然後處理以u為根節點的子樹,子樹處理完成後,將這棵子樹中的所有點的祖先都設成u,這時候處理跟u相關的所有詢問(u,v),如果vis[v]==true,那麼lca(u,v)就等於v當前設定的祖先。

舉個例子,示意圖如下:

對於正確性,這裡對於詢問v考慮三種情況:

v屬於u的子樹,這時v已經被訪問過,所以vis[v]==true。由於之前處理的了u的子節點的祖先都為u,所以v當前設定的祖先也是u,故lca(u,v)=u,很顯然是正確的。

v屬於u的祖先,既然是dfs過程,在訪問到u之前,u的祖先一定已經訪問過,所以vis[v]==true。因為當前處理的是v的子樹u,所以以v為根的子樹還沒有被處理完全,故這時候v的祖先還是它自己,所以lca(u,v)=v,正確。

v屬於u的兄弟節點或者u祖先的兄弟節點為根的子樹中,這樣v有可能被訪問過,也有可能沒有。沒有訪問過自然不需要處理,如果v被訪問了,那麼v當前設定的祖先是什麼呢?以上圖作為例子,假設u是5,v是3,在訪問到u的時候,v已經被訪問了,那麼根據演算法的步驟,此時v的祖先被設定成節點1,從圖中觀察,lca(u,v)確實就是節點1,故演算法正確。

其實tarjan演算法就是利用了樹的dfs的性質,從乙個節點v遍歷到另乙個節點u,肯定會經過lca(u,v),此時按照演算法將v的祖先就設定成lca(u,v),就可以保證演算法正確。至於尋找祖先的操作,很顯然就利用並查集來優化查詢速度。

#include 

using

namespace

std;

const

int maxn = 44444;

struct node ;

struct qnode ;

struct query ;

struct lca

void addedge(int u, int v, int w) );

tree[v].push_back((node) );

}void addquedge(int u, int v, int id) ;

qtree[u].push_back((qnode) );

qtree[v].push_back((qnode) );

}int find(int x)

void tarjan(int u)

for (int i = 0; i < (int)qtree[u].size(); i++) }}

} lca;

int main()

for (int i = 1; i <= m; i++)

lca.tarjan(1);

for (int i = 1; i <= m; i++)

}return

0;}

演算法學習筆記(LCA)

反正就記一記自己學到了什麼而已,加深印象 好吧其實現在只會倍增lca,好像還有其他的演算法 看oi wiki 但是暫時不想學 第一步 在樹上用dfs預處理出每乙個節點的father,用乙個二維陣列fa i,j 表示節點i往上跳2j步的祖先,由常識我們就知道fa i j fa fa i j 1 j 1...

演算法筆記 LCA問題 tarjan 離線演算法

reference 演算法流程 1 讀入表示父子關係的樹 2 儲存所有詢問 3 一次dfs回答所有詢問 演算法的關鍵是 對於乙個正在訪問中的節點u,其訪問過的子樹加上這個節點本身合併為乙個等價類,等價類有乙個代表元ancestor,值為u。對於u的乙個未訪問過的子節點v,如果屬於詢問 query v...

LCA離線演算法tarjan

lca演算法 lca least common ancestor 是指在一棵樹中,距離兩個點最近的兩者的公共節點。也就是說,在兩個點通往根的道路上,肯定會有公共的節點,我們就是要求找到公共的節點中,深度盡量深的點。還可以表示成另一種說法,就是如果把樹看成是乙個圖,這找到這兩個點中的最短距離。本文先介...