LCA的各類解法(三)——tarjan求LCA
阿新 • • 發佈:2018-12-08
一.概述.
tarjan求LCA是一種可以在的時間複雜度內處理離線LCA的方式.但是一旦遇到了線上詢問,tarjan求LCA就失效了.
二.演算法流程.
tarjan演算法本質上是對樸素LCA的一個優化,它是基於dfs進行優化的.
我們考慮開一個n個vector,其中q[i]表示與i相關的詢問點.
然後我們考慮一個dfs,當我們遍歷到點x的時候,列舉與x相關的所有詢問,當一個點y已經被遍歷過的時候,顯然y與x的LCA就是y的祖先中第一個被搜過但沒有退出搜尋的點.
為了維護這個過程,我們引入3種標記,其中0表示點未被搜過,1表示點被搜到但沒有退出搜尋,2表示點已經退出搜尋.
之後當遍歷一個點x時,列舉與x相關詢問時,就可以讓y從當前點開始往上爬,爬到的第一個標記為1的點即為這個詢問的LCA.這個演算法的時間複雜度最壞為.
為了優化這個演算法,我們可以考慮使用並查集,當一個點被標記為1時,就建立一個以它為根的並查集;當一個點被標記為2時,就把它所處的並查集併到它的父親上,這樣就可以做到.
至於為什麼並查集的log沒有算上,是因為這個並查集往上並的時候直接可以將根定為它的父親,這個操作的複雜度為,而get操作最多隻會將整個並查集遍歷一遍,時間複雜度之和最多為.
三.模板題及程式碼.
題目:luogu3379.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void #define pb push_back typedef long long LL; int ri(){ int x=0; char c; for (c=getchar();c<'0'||c>'9';c=getchar()); for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0'; return x; } const int N=500000; struct side{ int y,next; }e[N*2+9]; int n,m,lin[N+9],top,root; vector<int>q[N+9],id[N+9]; int ans[N+9]; int fa[N+9],vis[N+9]; int get(int u){return fa[u]^u?fa[u]=get(fa[u]):u;} void ins(int x,int y){ e[++top].y=y; e[top].next=lin[x]; lin[x]=top; } void dfs(int k){ fa[k]=k; vis[k]=1; for (int i=lin[k];i;i=e[i].next) if (!vis[e[i].y]){ dfs(e[i].y); fa[e[i].y]=k; } vis[k]=2; int len=q[k].size(); for (int i=0;i<len;++i) if (vis[q[k][i]]==2) ans[id[k][i]]=get(q[k][i]); } Abigail into(){ n=ri();m=ri();root=ri(); int x,y; for (int i=1;i<n;++i){ x=ri();y=ri(); ins(x,y);ins(y,x); } for (int i=1;i<=m;++i){ x=ri();y=ri(); q[x].pb(y);id[x].pb(i); q[y].pb(x);id[y].pb(i); } } Abigail work(){ dfs(root); } Abigail outo(){ for (int i=1;i<=m;++i) printf("%d\n",ans[i]); } int main(){ into(); work(); outo(); return 0; }