離線處理,LCA的Tarjan演算法 HDU 2586
阿新 • • 發佈:2018-12-21
首先,什麼是離線演算法,線上演算法呢。在查詢問題中,離線演算法需要先將所有查詢全部輸入,然後統一計算,最後再同意輸出。就比如下面介紹的Tarjan求LCA;線上演算法就是像前面樹上倍增求LCA一樣,預處理(求倍增過程中所需要的量們)所花費的時間相對於整個程式來說較大,而查詢速度相對整個程式來說很快。
LCA的Tarjan演算法的核心是“向上標記”,即不同的點有不同的標記,沒訪問的點無標記,即為“0”、訪問過但沒有回溯的點標記為“1”、訪問過並且回溯過的點標記為“2”。然後在dfs序的支配下,一個即將被回溯的“1”節點與當前樹中任意一個回溯過的“2”節點的最近公共祖先LCA即為:“2”點到根節點的路徑上的第一個“1”節點。
最後找“2”型節點到根節點路徑上第一個“1”型節點的操作,可以用並查集優化,即那個“1”型點就是並查集中對應聯通塊中的代表元素:find(x)。
PS:補充小知識,並查集的時間複雜度可以近似為o(1)。
例題:HDU2586 http://acm.hdu.edu.cn/showproblem.php?pid=2586
AC程式碼:
/*離線演算法,Tarjan求LCA,複雜度o(n+m)*/ #include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include<algorithm> #include <set> #include <queue> #include <stack> #include<vector> #include<map> #include<ctime> #define ll long long using namespace std; const int SIZE=50010; int ver[2*SIZE],Next[2*SIZE],edge[2*SIZE],head[SIZE];//鄰接表 int fat[SIZE],dist[SIZE],v[SIZE],ans[SIZE];//並查集fat、節點距離根節點的距離、標記陣列、兩點間距離 vector <int> query[SIZE],query_id[SIZE];//某次查詢的x、y;該查詢的 查詢標號 int T,n,m,tot;//多組輸入、n、嗎、鄰接表邊數、 void add(int x,int y,int z) { ver[++tot]=y; edge[tot]=z; Next[tot]=head[x]; head[x]=tot; } void add_query(int x,int y,int id)//離線:新增查詢 { query[x].push_back(y); query_id[x].push_back(id); query[y].push_back(x); query_id[y].push_back(id); } int find(int x) { if(x==fat[x])return fat[x]; return fat[x]=find(fat[x]); } void unionn(int x,int y) { int fa=find(x),fb=find(y); if(fa!=fb) { fat[fa]=fb; } } void tarjan(int x)//dfs { v[x]=1; for(int i=head[x];i;i=Next[i]) { int y=ver[i]; if(v[y])continue; dist[y]=dist[x]+edge[i];//dfs求樹中節點到根節點的距離,PS:不是深度。 tarjan(y); unionn(y,x); } for(int i=0;i<query[x].size();++i) { int y=query[x][i],id=query_id[x][i]; if(v[y]==2) { int lca=find(y);//x、y的公共祖先 ans[id]=dist[x]+dist[y]-2*dist[lca];//公式求答案 //ans[id]=min(ans[id],dist[x]+dist[y]-2*dist[lca]);書上這麼寫的,但沒必要 } } v[x]=2; } int main() { cin>>T; while(T--) { cin>>n>>m; for(int i=1;i<=n;++i) { head[i]=0; fat[i]=i;//一系列初始化; v[i]=0; query[i].clear(); query_id[i].clear(); } tot=0; for(int i=1;i<n;++i) { int x,y,z; cin>>x>>y>>z; add(x,y,z); add(y,x,z); } for(int i=1;i<=m;++i)//離線 { int x,y; cin>>x>>y; if(x==y)ans[i]=0; else { add_query(x,y,i); ans[i]=(1<<30); } } tarjan(1);//離線 for(int i=1;i<=m;++i)cout<<ans[i]<<endl; } return 0; }
The end;