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 } 
关于板子,它救活了

 

posted @ 2018-08-16 16:19  YuWenjue  阅读(337)  评论(0编辑  收藏  举报