最近公共祖先(LCA)

2022-09-21 12:30:12 字數 1619 閱讀 4399

一道板題

維護陣列 \(f[ i ][ j ]\) 表示\(i\) 號節點的第\(2^j\) 級祖先,沒有則為\(0\)

轉移方程:

\[f[i][j] =f[f[i][j-1]][j-1] (j>0)

\]\[f[i][j]=fa[i] (j=0)

\]將k從大到小迴圈,如果\(x\),\(y\) 的\(2^k\) 級祖先相同則不做改變,不同則將 \(x\) ,\(y\) 更新為各自的\(2^k\) 級祖先。

當 \(k=0\) 時,lca為此時 \(x\) ,\(y\) 的父親

令 \(dep[a]>dep[b]\) ,將 \(a\) 向上跳,直到 \(a\) 的深度與 \(b\) 的深度相同為止,於是變轉換為相同深度求 lca

時間複雜度:

預處理: \(o(nlogn)\) ,單詞詢問: \(o(logn)\)

空間複雜度: \(nlogn\)

**示例:

預處理:

void bfs(int root)

} }}

主體:

int lca(int a,int b)
利用遞迴的回溯做標記,遍歷一次後標記為\(1\) ,回溯後標記為\(2\)

把詢問轉換成離線做法,先把所有的詢問存下來,最後統一輸出

優化用到了並查集的思想,回溯後將此點與其父親節點合併,答案返回集合的父親節點

**示例:

存詢問:

void add_qu(int a,int b,int i)
主體:

void targan(int x)

for(int i=0;i時間複雜度: \(o(n+m)\)

還是貼個完整**吧:

#includeusing namespace std;

const int n=5e5+5,m=n*2;

int h[n],nxt[m],en[m],m;

vectorqu[n],id[n];

int root;

int v[n],ans[n],f[n];

void add(int a,int b)

void add_qu(int a,int b,int i)

int get(int x)

void targan(int x)

for(int i=0;iv[x]=2;

}int main()

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

f[root]=root;

targan(root);

for(int i=1;i<=k;i++) printf("%d\n",ans[i]);

return 0;

}

最近公共祖先 LCA 最近公共祖先

直接暴力搜尋參考 普通搜尋每次查詢都需要 樸素演算法是一層一層往上找,倍增的話直接預處理出乙個 具體做法是 維護乙個 的關係來線性求出這個陣列 int anc n 31 int dep n 記錄節點深度 void dfs int u,int parent for int i 0 i g u size...

最近公共祖先 最近公共祖先(LCA)

如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。輸入格式 第一行包含三個正整數n m s,分別表示樹的結點個數 詢問的個數和樹根結點的序號。接下來n 1行每行包含兩個正整數x y,表示x結點和y結點之間有一條直接連線的邊 資料保證可以構成樹 接下來m行每行包含兩個正整數a b,表示詢問...

LCA 最近公共祖先

定義 對於有根樹t的兩個結點u v,最近公共祖先lca t,u,v 表示乙個結點x,滿足x是u v的祖先且x的深度盡可能大。另一種理解方式是把t理解為乙個無向無環圖,而lca t,u,v 即u到v的最短路上深度最小的點。現在給定乙個根為1的樹,求某兩個點的最近公共祖先。思路 預處理出每個點的深度再一...