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

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 }

  

时间: 2024-08-05 06:38:12

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

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

[图论] LCA(最近公共祖先)Tarjan 离线算法

很好的参考资料:http://taop.marchtea.com/04.04.html    下面的配图和部分文字转载于此文章 离线算法就是指统一输入后再统一输出,而不是边输入边实时输出.Tarjan算法的复杂度为O(N+Q),Q为询问的次数. 由于是离线算法,所以要保存输入的信息,次序问题. 若两个结点u.v分别分布于某节点t 的左右子树,那么此节点 t即为u和v的最近公共祖先.更进一步,考虑到一个节点自己就是LCA的情况,得知: ?若某结点t 是两结点u.v的祖先之一,且这两结点并不分布于该

POJ 1330 最近公共祖先LCA(Tarjan离线做法)

题目链接:http://poj.org/problem?id=1330 题目大意十分明了简单,就是给定一棵树,求某两个子节点的最近公共祖先,如果尚不清楚LCA的同学,可以左转百度等进行学习. 稍微需要注意的是,建树顺序需要按照题目给定的顺序进行,也就是说根被设定成第一个给出的结点,如样例2. 此题网上题解颇多,但是多是使用的邻接表存图,于是我这里采用了边表,不过实质上Tarjan的部分思想都是一样的,均利用了并查集. AC代码: #include <cstdio> #include <c

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问

POJ 1330 Nearest Common ancesters(LCA,Tarjan离线算法)

Description: In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16.

笔记:LCA最近公共祖先 Tarjan(离线)算法

LCA最近公共祖先 Tarjan他贱(离线)算法的基本思路及其算法实现 本文是网络资料整理或部分转载或部分原创,参考文章如下: https://www.cnblogs.com/JVxie/p/4854719.html http://blog.csdn.net/ywcpig/article/details/52336496 https://baike.baidu.com/item/最近公共祖先/8918834?fr=aladdin 最近公共祖先简称LCA(Lowest Common Ancesto

算法详解之最近公共祖先(LCA)

若图片出锅请转至here 概念 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而LCA还可以将自己视为祖先节点. 举个