码迷,mamicode.com
首页 > 编程语言 > 详细

【C++】最近公共祖先LCA(Tarjan离线算法)&& 洛谷P3379LCA模板

时间:2017-12-02 19:04:52      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:并查集   前向星   org   div   style   strong   复杂度   比较   最近公共祖先   

1.前言

  首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为)。

2.思想

  下面详细介绍一下Tarjan算法的思想:

      1.任选一个点为根节点,从根节点开始。

      2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

      3.若是v还有子节点,返回2,否则下一步。

      4.合并v到u上。

      5.寻找与当前点u有询问关系的点v。

      6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。

  从上面步骤可以看出,Tarjan算法要用到并查集。这里,我们使用链式前向星来存储边和询问情况。

3.算法实现

不妨以洛谷P3379:LCA模板题为例题

 1 #include<cstdio>
 2 #include<cstring>
 3 
 4 const int maxn = 5e5 + 10,maxm = 5e5 + 10;
 5 int fa[maxn],fir[maxn],frt[maxn],ans[maxm],n,m,s,tot,id;
 6 bool flag[maxn];
 7 //存边
 8 struct Edge{
 9     int v,next;
10 }e[maxn << 1];
11 //存询问
12 struct Ques{
13     int v,next,qid;
14 }q[maxm << 1];
15 //加边
16 void addEdge(int u,int v){
17     e[tot] = (Edge){v,fir[u]};
18     fir[u] = tot++;
19     e[tot] = (Edge){u,fir[v]};
20     fir[v] = tot++;
21 }
22 //加询问
23 void addQues(int u,int v,int qid){
24     q[id] = (Ques){v,frt[u],qid};
25     frt[u] = id++;
26     q[id] = (Ques){u,frt[v],qid};
27     frt[v] = id++;
28 }
29 //读入优化
30 inline int read(){
31     int sum = 0;
32     char ch = getchar();
33     while(ch < 0 || ch > 9) ch = getchar();
34     while(ch >= 0 && ch <= 9){sum = sum * 10 + ch - 0; ch = getchar();}
35     return sum;
36 }
37 //输出优化
38 void write(int x){
39     if(x / 10) write(x / 10);
40     putchar(x % 10 + 0);
41 }
42 //寻找父亲
43 int find(int x){
44     if(fa[x] != x) fa[x] = find(fa[x]);
45     return fa[x];
46 }
47 //合并
48 void unionn(int x,int y){
49     int fx = find(x),fy = find(y);
50     fa[fx] = fy;
51 }
52 //离线操作(u表示当前点,fa表示该点的前驱点)
53 void dfs(int u,int fa){
54     flag[u] = true;//标记已访问
55     //遍历所有与u相连的点
56     for(int i = fir[u];i != -1;i = e[i].next){
57         int v = e[i].v;
58         //如果v不是u的前驱
59         if(v != fa){
60             dfs(v,u);//遍历下一层
61             //寻找是否有询问关系
62             for(int j = frt[v];j != -1;j = q[j].next){
63                 int k = q[j].v;
64                 if(flag[k])
65                     ans[q[j].qid] = find(k);
66             }
67             unionn(v,u);//连接
68         }
69     }
70 }
71 //主函数
72 int main(){
73     n = read(),m = read(),s = read();
74     memset(fir,-1,sizeof(fir));
75     memset(frt,-1,sizeof(frt));
76     for(int i = 1;i < n;i++){
77         fa[i] = i;
78         int x = read(),y = read();
79         addEdge(x,y);
80     }
81     fa[n] = n;
82     for(int i = 1;i <= m;i++){
83         int x = read(),y = read();
84         addQues(x,y,i);
85     }
86     dfs(s,-1);
87     //因为这里已经给出根了,直接从根开始
88     for(int i = 1;i <= m;i++,putchar(\n)) write(ans[i]);
89     return 0;
90 }

 

 

  

【C++】最近公共祖先LCA(Tarjan离线算法)&& 洛谷P3379LCA模板

标签:并查集   前向星   org   div   style   strong   复杂度   比较   最近公共祖先   

原文地址:http://www.cnblogs.com/water-mi/p/7774649.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!