Tarjan求LCA
LCA问题算是一类比较经典的树上的问题
做法比较多样
比如说暴力啊,倍增啊等等
今天在这里给大家讲一下tarjan算法!
tarjan求LCA是一种稳定高速的算法
时间复杂度能做到预处理O(n + m),查询O(1)
它的主要思想是dfs和并查集
1.输入数据,找出根节点(或输入的)并将图存起来
2.输入需要查找的每一对点(两个点),也存起来(也存成图)
3.从根节点开始向它的每一个孩子节点进行深搜
4.同时开一个bool类型的数组记录此节点是否搜索过
5.搜索到p节点时先将p标记为已经搜索过了
6.然后遍历所有与p相连的节点,并标记为已经搜索过了
7.接着将p的子节点和p合并(此处要用到并查集)
8.然后遍历所有和p有询问关系的p的子节点
9.若该子节点已经遍历过,则一定可以将该子节点和p的父亲节点合并
可能还是有很多人并没有完全理解这段文字叙述的算法过程
下面就直接上代码(注释很详细)
- #include<cstdio>
- #include<cstring>
- #include<cstdlib>
- #include<cctype>
- #include<cmath>
- #include<string>
- #include<iostream>
- #include<algorithm>
- #include<map>
- #include<set>
- #include<queue>
- #include<stack>
- #include<vector>
- using namespace std;
- typedef long long ll;
- const int maxn = 5e5 + ;
- int read()
- {
- int ans = , op = ;
- char ch = getchar();
- while(ch < '' || ch > '')
- {
- if(ch == '-') op = -;
- ch = getchar();
- }
- while(ch >= '' && ch <= '')
- {
- ans *= ;
- ans += ch - '';
- ch = getchar();
- }
- return ans * op;
- }
- struct Drug
- {
- int next, to, lca;
- }edge[maxn<<], qedge[maxn<<];//edge[N]为树的链表;qedge[N]为需要查询LCA的两节点的链表
- int n, m, s, x, y;
- int num_edge, num_qedge, head[maxn], qhead[maxn], father[maxn];
- bool visit[maxn];//判断是否被找过
- void add_edge(int from, int to)//建立树的链表
- {
- edge[++num_edge].next = head[from];
- edge[num_edge].to = to;
- head[from] = num_edge;
- // printf("#%d #%d #%d #%d\n", num_edge, head[from], from, edge[num_edge].next);
- }
- void add_qedge(int from, int to)//建立需要查询LCA的两节点的链表
- {
- qedge[++num_qedge].next = qhead[from];
- qedge[num_qedge].to = to;
- qhead[from] = num_qedge;
- }
- int find(int x)//找爹函数
- {
- if(father[x] ^ x) father[x] = find(father[x]);
- return father[x];
- }
- void dfs(int x)//把整棵树的一部分看作以节点x为根节点的小树, x的初始值为s;
- {
- father[x] = x;//由于节点x被看作是根节点,所以把x的father设为它自己
- visit[x] = ;//标记为已被搜索过
- for(int k = head[x]; k ; k=edge[k].next)//遍历所有与x相连的节点
- {
- if(!visit[edge[k].to])//若未被搜索
- {
- dfs(edge[k].to);//以该节点为根节点搞小树
- father[edge[k].to] = x;//把x的孩子节点的father重新设为x
- }
- }
- for(int k = qhead[x]; k ; k = qedge[k].next)//搜索包含节点x的所有询问
- {
- if(visit[qedge[k].to])//如果另一节点已被搜索过
- {
- qedge[k].lca = find(qedge[k].to);
- //把另一节点的祖先设为这两个节点的最近公共祖先
- if(k & ) qedge[k + ].lca = qedge[k].lca;
- //由于将每一组查询变为两组,所以2n-1和2n的结果是一样的
- else qedge[k - ].lca = qedge[k].lca;
- }
- }
- }
- int main(){
- n = read(), m = read(), s = read();//输入节点数,查询数和根节点
- for(int i = ;i < n;i++)
- {
- x = read(), y = read();//输入每条边
- add_edge(x, y);
- add_edge(y, x);
- }
- for(int i = ;i <= m;i++)
- {
- x = read(), y = read();
- //输入每次查询,考虑(u,v)时若查找到u但v未被查找,所以将(u,v)(v,u)全部记录
- add_qedge(x, y);
- add_qedge(y, x);
- }
- dfs(s);
- for(int i = ;i <= m;i++) printf("%d\n", qedge[i << ].lca);//两者结果一样,只输出一组即可
- // printf("%d", num_edge);
- return ;
- }
Tarjan求LCA的更多相关文章
- 【Tarjan】洛谷P3379 Tarjan求LCA
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- 倍增\ tarjan求lca
对于每个节点v,记录anc[v][k],表示从它向上走2k步后到达的节点(如果越过了根节点,那么anc[v][k]就是根节点). dfs函数对树进行的dfs,先求出anc[v][0],再利用anc[v ...
- 详解使用 Tarjan 求 LCA 问题(图解)
LCA问题有多种求法,例如倍增,Tarjan. 本篇博文讲解如何使用Tarjan求LCA. 如果你还不知道什么是LCA,没关系,本文会详细解释. 在本文中,因为我懒为方便理解,使用二叉树进行示范. L ...
- 倍增 Tarjan 求LCA
...
- SPOJ 3978 Distance Query(tarjan求LCA)
The traffic network in a country consists of N cities (labeled with integers from 1 to N) and N-1 ro ...
- tarjan求lca的神奇
题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...
- Tarjan求LCA(离线)
基本思想 把要求的点对保存下来,在dfs时顺带求出来. 方法 将每个已经遍历的点指向它回溯的最高节点(遍历它的子树时指向自己),每遍历到一个点就处理它存在的询问如果另一个点已经遍历,则lca就是另一个 ...
- 图论分支-倍增Tarjan求LCA
LCA,最近公共祖先,这是树上最常用的算法之一,因为它可以求距离,也可以求路径等等 LCA有两种写法,一种是倍增思想,另一种是Tarjan求法,我们可以通过一道题来看一看, 题目描述 欢乐岛上有个非常 ...
- Tarjan求LCA总结
Tarjan算法向上标记法:从x向上走到根节点,并标记所有经过的点从y向上走到根节点,当第一次遇到已标记的节点时,就找到了LCA(x, y)对于每个询问,向上标记法的时间复杂度最坏为O(n) 在深度遍 ...
随机推荐
- 在VS2010上安装MVC4(webApi)
我们安装的VS2010上是没有MVC4或者WebApi的,要想加入这些功能只能自己在网上下载安装. 要安装MVC4,首先得安装VS10sp(Service Package)1,然后再安装MVC4.安装 ...
- ubantu安全卸载火狐浏览器
首先查看浏览器相应的安装包,命令: dpkg --get-selections |grep firefox 2.删除相面出现的包的命令: sudo apt-get purge pck1-name p ...
- leetcode 91 Decode Ways I
令dp[i]为从0到i的总方法数,那么很容易得出dp[i]=dp[i-1]+dp[i-2], 即当我们以i为结尾的时候,可以将i单独作为一个字母decode (dp[i-1]),同时也可以将i和i-1 ...
- spring集成rabbitmq
https://www.cnblogs.com/nizuimeiabc1/p/9608763.html
- 有关svn的报错
由于目标计算机积极拒绝,无法连接.当报出这样的错的时候就是跨域的问题
- IP欺骗
通过一番测试,我发现当我连续提交3份问卷,再换一个IP提交3个问卷,也就是连续提交了6份问卷,并没有触发网站的反爬虫机制.所以我们可以猜测对方基于IP提交问卷的频率来识别爬虫程序的.看到这里,大家可能 ...
- ES6 promise学习
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大. 1.promise是一构造函数,既然是构造函数,那么我们就可以用 new Promise()得到一个p ...
- vue菜鸟从业记:完成项目最后一公里之真机测试和打包上线
最近我朋友王小闰他们公司的项目开发已经进入收尾阶段,前后端并行开发的差不多了,联调也调过了,上篇文章里也讲到了,所谓联调,就仿佛在说“我也不知道我的接口文档写的对不对,我们验证一下吧?我也不知道我的数 ...
- 需要优化代码的leetcode
1 关于验证字符串的问题: 2
- kubernetes in action - Pods
Pods 在上一篇也说明了,pods是kubernetes的最小部署单元,并且所有在pods中的container共享namespaces 那么为什么需要pods这样的概念? 因为在实际中,我们有一种 ...