標籤:getc namespace 記錄 getch 很多 amp 代碼 read typedef
LCA問題算是一類比較經典的樹上的問題
做法比較多樣
比如說暴力啊,倍增啊等等
今天在這裡給大家講一下tarjan演算法!
tarjan求LCA是一種穩定高速的演算法
時間複雜度能做到預先處理O(n + m),查詢O(1)
它的主要思想是dfs和並查集
1.輸入資料,找出根節點(或輸入的)並將圖存起來
2.輸入需要尋找的每一對點(兩個點),也存起來(也存成圖)
3.從根節點開始向它的每一個孩子節點進行深搜
4.同時開一個bool類型的數組記錄此節點是否搜尋過
5.搜尋到p節點時先將p標記為已經搜尋過了
6.然後遍曆所有與p相連的節點,並標記為已經搜尋過了
7.接著將p的子節點和p合并(此處要用到並查集)
8.然後遍曆所有和p有詢問關係的p的子節點
9.若該子節點已經遍曆過,則一定可以將該子節點和p的父親節點合并
可能還是有很多人並沒有完全理解這段文字敘述的演算法過程
下面就直接上代碼(注釋很詳細)
#include<cstdio>#include<cstring>#include<cstdlib>#include<cctype>#include<cmath>#include<string>#include<iostream>#include<algorithm>#include<map>#include<set>#include<queue>#include<stack>#include<vector>using namespace std;typedef long long ll;const int maxn = 5e5 + 5;int read(){ int ans = 0, op = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op;}struct Drug{ int next, to, lca;}edge[maxn<<1], qedge[maxn<<1];//edge[N]為樹的鏈表;qedge[N]為需要查詢LCA的兩節點的鏈表int n, m, s, x, y;int num_edge, num_qedge, head[maxn], qhead[maxn], father[maxn];bool visit[maxn];//判斷是否被找過 void add_edge(int from, int to)//建立樹的鏈表 { edge[++num_edge].next = head[from]; edge[num_edge].to = to; head[from] = num_edge;// printf("#%d #%d #%d #%d\n", num_edge, head[from], from, edge[num_edge].next);}void add_qedge(int from, int to)//建立需要查詢LCA的兩節點的鏈表 { qedge[++num_qedge].next = qhead[from]; qedge[num_qedge].to = to; qhead[from] = num_qedge;}int find(int x)//找爹函數 { if(father[x] ^ x) father[x] = find(father[x]); return father[x];}void dfs(int x)//把整棵樹的一部分看作以節點x為根節點的小樹, x的初始值為s; { father[x] = x;//由於節點x被看作是根節點,所以把x的father設為它自己 visit[x] = 1;//標記為已被搜尋過 for(int k = head[x]; k ; k=edge[k].next)//遍曆所有與x相連的節點 { if(!visit[edge[k].to])//若未被搜尋 { dfs(edge[k].to);//以該節點為根節點搞小樹 father[edge[k].to] = x;//把x的孩子節點的father重新設為x } } for(int k = qhead[x]; k ; k = qedge[k].next)//搜尋包含節點x的所有詢問 { if(visit[qedge[k].to])//如果另一節點已被搜尋過 { qedge[k].lca = find(qedge[k].to); //把另一節點的祖先設為這兩個節點的最近公用祖先 if(k & 1) qedge[k + 1].lca = qedge[k].lca; //由於將每一組查詢變為兩組,所以2n-1和2n的結果是一樣的 else qedge[k - 1].lca = qedge[k].lca; } }}int main(){ n = read(), m = read(), s = read();//輸入節點數,查詢數和根節點 for(int i = 1;i < n;i++) { x = read(), y = read();//輸入每條邊 add_edge(x, y); add_edge(y, x); } for(int i = 1;i <= m;i++) { x = read(), y = read(); //輸入每次查詢,考慮(u,v)時若尋找到u但v未被尋找,所以將(u,v)(v,u)全部記錄 add_qedge(x, y); add_qedge(y, x); } dfs(s); for(int i = 1;i <= m;i++) printf("%d\n", qedge[i << 1].lca);//兩者結果一樣,只輸出一組即可// printf("%d", num_edge); return 0;}
Tarjan求LCA