Tarjan求LCA总结
Tarjan算法
向上标记法:
从x向上走到根节点,并标记所有经过的点
从y向上走到根节点,当第一次遇到已标记的节点时,就找到了LCA(x, y)
对于每个询问,向上标记法的时间复杂度最坏为O(n)
在深度遍历的任意时刻,我们将树中的节点分成三类:
1.我们已经访问了,但是我们还没有回溯的节点标记为1
2.我们访问过并且已经回溯到的,标记为2
3.没有访问过的节点
对于正在访问的节点x,他的父节点是标记为1的。若y是已经访问并且回溯的节点,则LCA(x, y)就是由y向上走,遇到的第一个标记为1的节点。
我们很容易想到可以使用并查集优化。
当一个节点标记为2时,我们把它合并到他父亲所在的集合(此时他的父亲一定标记为1且单独构成一个集合)
这就相当于每个完成回溯的几点都有一个指向它的父节点的指针,只需查询y所在集合的代表元素(并查集的get操作),就等价于从y向上一直走到一个开始递归但未回溯的节点,即LCA(x, y)
其实整个过程,自己在演草纸上画一遍就好了(建议换一篇博客看看)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 500086; 4 struct shiki { 5 int y, net; 6 }e[maxn << 1]; 7 struct enkidu { 8 int self, id, nex; 9 }ask[maxn << 1]; 10 int n, m, s; 11 int lin[maxn], len = 0; 12 int both[maxn], tot = 0; 13 int fa[maxn], lca[maxn]; 14 int vis[maxn]; 15 16 inline int read() { 17 int x = 0, y = 1; 18 char ch = getchar(); 19 while(!isdigit(ch)) { 20 if(ch == '-') y = -1; 21 ch = getchar(); 22 } 23 while(isdigit(ch)) { 24 x = (x << 1) + (x << 3) + ch - '0'; 25 ch = getchar(); 26 } 27 return x * y; 28 } 29 30 inline void insert(int xx, int yy) { 31 e[++len].y = yy; 32 e[len].net = lin[xx]; 33 lin[xx] = len; 34 } 35 36 inline void add(int xx, int yy, int i) { 37 ask[++tot].self = yy; 38 ask[tot].id = i; 39 ask[tot].nex = both[xx]; 40 both[xx] = tot; 41 } 42 43 int getfather(int x) { 44 if(x == fa[x]) return x; 45 return fa[x] = getfather(fa[x]); 46 } 47 48 void LCA_tarjan(int x) { 49 vis[x] = 1; 50 for(int i = lin[x]; i; i = e[i].net) { 51 int to = e[i].y; 52 if(vis[to]) continue; 53 LCA_tarjan(to); 54 fa[to] = x; 55 } 56 for(int i = both[x]; i; i = ask[i].nex) { 57 int to = ask[i].self; 58 if(vis[to] == 2) 59 lca[ask[i].id] = getfather(to); 60 } 61 vis[x] = 2; 62 } 63 64 int main() { 65 memset(vis, 0, sizeof(vis)); 66 n = read(), m = read(), s = read(); 67 for(int i = 1; i < n; ++i) { 68 int x, y; 69 x = read(), y = read(); 70 insert(x, y); 71 insert(y, x); 72 } 73 for(int i = 1; i <= n; ++i) fa[i] = i; 74 for(int i = 1; i <= m; ++i) { 75 int x, y; 76 x = read(), y = read(); 77 add(x, y, i); 78 add(y, x, i); 79 } 80 LCA_tarjan(s); 81 for(int i = 1; i <= m; ++i) 82 cout << lca[i] << '\n'; 83 return 0; 84 }