【模板】LCA(tarjan)

 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 const int N = 10010;
 5 vector<int>tree[N];
 6 vector<int>que[N];
 7 int par[N];
 8 int anc[N];
 9 int in[N];
10 bool visited[N];
11 
12 int getpar(int x){
13     if(par[x] != x)
14         par[x] = getpar(par[x]);
15     return par[x];
16 }
17 void merge(int x, int y){
18     int fx = getpar(x);
19     int fy = getpar(y);
20     if(fx == fy) return;
21     else
22         par[fx] = fy;  
23 }
24 void tarjan(int x){
25     for(int i = 0; i < tree[x].size(); i++){
26         tarjan(tree[x][i]);
27         merge(tree[x][i], x);
28         anc[getpar(x)] = x;
29     }
30     visited[x] = true;
31     for(int i = 0; i < que[x].size(); i++){
32         if(visited[que[x][i]])
33             cout<<x<<" "<<que[x][i]<<" "<<anc[getpar(que[x][i])]<<endl;
34     }
35 }
36 int main(){
37     int n, i;
38     cin>>n;
39     for(i = 1; i < n; i++){
40         int x, y;
41         cin>>x>>y;
42         tree[x].push_back(y);
43         in[y]++;
44     }
45     int root;
46     for(i = 1; i <= n; i++){
47         par[i] = i;
48         anc[i] = i;
49         if(in[i]==0){
50             root = i;
51             break;
52         } 
53     }
54     int m;
55     cin>>m;
56     for(i = 1; i <= m; i++){
57         int x, y;
58         cin>>x>>y;
59         que[x].push_back(y);
60         que[y].push_back(x);
61     }
62     tarjan(root);
63     return 0;
64 }

备注:

终于搞明白了tarjan找LCA~撒花!

以上是我“自认为美的代码”,完全按自己的代码风格写的,感觉比网上的代码都要简洁清楚哈哈哈。

看了好多资料,很多都写得很不清楚啊,代码也乱七八糟的。http://www.mamicode.com/info-detail-1067269.html的描述颇为详细,也是让我思路清晰起来的一篇。

tarjan找lca是离线的,所谓离线查询,就是指必须事先读入要查询的所有点对,因为遍历的过程就要进行查询。

这个算法,精髓在于,利用了dfs的访问顺序。当遍历完一棵子树时,回溯到这棵子树的根节点,把这棵子树合并为一个集合(并查集实现),也就是说子树的根节点就是当前节点。然后进行查询,如果此时要查询的另一个节点,如果已经访问过,那么它的祖先(anc)就是它们俩的lca。这就是最近的。

补充:比如说这幅我亲手绘的图。。虽然比较丑

当前访问完的点是6,即x为6。这时假如6还有子结点,那它们现在都合并在了6上,虽然这跟我们即将要进行的查询毫无关系。

假设我们要查询的点是4(求4和6的lca),为啥我们搜完4的时候没求出来呢,因为那会儿6还没有访问到。但现在情况就不一样了,4已经访问过了,并且现在右边橘黄色的大圈是一个大子树,4的祖先是1,而6是从1走到的,所以4和6的lca就是1.

所以,我刚才没理解的点就是,合并操作和当前要查询的点和partner没有关系,而在查询它的parner和它时才派上用场虽然看表述只是交换了一下位置。。但其实“交换律”也是离线查询的精髓所在。。

复杂度是O(m+n),及查询数加节点数。

不要忘了初始化par和anc数组。

还有就是注意一下输入都是有向边。tarjan是从一个入度为1,即整棵树根节点开始。

posted @ 2016-11-09 20:13  timeaftertime  阅读(334)  评论(0编辑  收藏  举报