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的父亲节点合并

可能还是有很多人并没有完全理解这段文字叙述的算法过程

下面就直接上代码(注释很详细)

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<cstdlib>
  4. #include<cctype>
  5. #include<cmath>
  6. #include<string>
  7. #include<iostream>
  8. #include<algorithm>
  9. #include<map>
  10. #include<set>
  11. #include<queue>
  12. #include<stack>
  13. #include<vector>
  14. using namespace std;
  15. typedef long long ll;
  16. const int maxn = 5e5 + ;
  17. int read()
  18. {
  19. int ans = , op = ;
  20. char ch = getchar();
  21. while(ch < '' || ch > '')
  22. {
  23. if(ch == '-') op = -;
  24. ch = getchar();
  25. }
  26. while(ch >= '' && ch <= '')
  27. {
  28. ans *= ;
  29. ans += ch - '';
  30. ch = getchar();
  31. }
  32. return ans * op;
  33. }
  34. struct Drug
  35. {
  36. int next, to, lca;
  37. }edge[maxn<<], qedge[maxn<<];//edge[N]为树的链表;qedge[N]为需要查询LCA的两节点的链表
  38. int n, m, s, x, y;
  39. int num_edge, num_qedge, head[maxn], qhead[maxn], father[maxn];
  40. bool visit[maxn];//判断是否被找过
  41. void add_edge(int from, int to)//建立树的链表
  42. {
  43. edge[++num_edge].next = head[from];
  44. edge[num_edge].to = to;
  45. head[from] = num_edge;
  46. // printf("#%d #%d #%d #%d\n", num_edge, head[from], from, edge[num_edge].next);
  47. }
  48. void add_qedge(int from, int to)//建立需要查询LCA的两节点的链表
  49. {
  50. qedge[++num_qedge].next = qhead[from];
  51. qedge[num_qedge].to = to;
  52. qhead[from] = num_qedge;
  53. }
  54. int find(int x)//找爹函数
  55. {
  56. if(father[x] ^ x) father[x] = find(father[x]);
  57. return father[x];
  58. }
  59. void dfs(int x)//把整棵树的一部分看作以节点x为根节点的小树, x的初始值为s;
  60. {
  61. father[x] = x;//由于节点x被看作是根节点,所以把x的father设为它自己
  62. visit[x] = ;//标记为已被搜索过
  63. for(int k = head[x]; k ; k=edge[k].next)//遍历所有与x相连的节点
  64. {
  65. if(!visit[edge[k].to])//若未被搜索
  66. {
  67. dfs(edge[k].to);//以该节点为根节点搞小树
  68. father[edge[k].to] = x;//把x的孩子节点的father重新设为x
  69. }
  70. }
  71. for(int k = qhead[x]; k ; k = qedge[k].next)//搜索包含节点x的所有询问
  72. {
  73. if(visit[qedge[k].to])//如果另一节点已被搜索过
  74. {
  75. qedge[k].lca = find(qedge[k].to);
  76. //把另一节点的祖先设为这两个节点的最近公共祖先
  77. if(k & ) qedge[k + ].lca = qedge[k].lca;
  78. //由于将每一组查询变为两组,所以2n-1和2n的结果是一样的
  79. else qedge[k - ].lca = qedge[k].lca;
  80. }
  81. }
  82. }
  83. int main(){
  84. n = read(), m = read(), s = read();//输入节点数,查询数和根节点
  85. for(int i = ;i < n;i++)
  86. {
  87. x = read(), y = read();//输入每条边
  88. add_edge(x, y);
  89. add_edge(y, x);
  90. }
  91. for(int i = ;i <= m;i++)
  92. {
  93. x = read(), y = read();
  94. //输入每次查询,考虑(u,v)时若查找到u但v未被查找,所以将(u,v)(v,u)全部记录
  95. add_qedge(x, y);
  96. add_qedge(y, x);
  97. }
  98. dfs(s);
  99. for(int i = ;i <= m;i++) printf("%d\n", qedge[i << ].lca);//两者结果一样,只输出一组即可
  100. // printf("%d", num_edge);
  101. return ;
  102. }

 

Tarjan求LCA的更多相关文章

  1. 【Tarjan】洛谷P3379 Tarjan求LCA

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  2. 倍增\ tarjan求lca

    对于每个节点v,记录anc[v][k],表示从它向上走2k步后到达的节点(如果越过了根节点,那么anc[v][k]就是根节点). dfs函数对树进行的dfs,先求出anc[v][0],再利用anc[v ...

  3. 详解使用 Tarjan 求 LCA 问题(图解)

    LCA问题有多种求法,例如倍增,Tarjan. 本篇博文讲解如何使用Tarjan求LCA. 如果你还不知道什么是LCA,没关系,本文会详细解释. 在本文中,因为我懒为方便理解,使用二叉树进行示范. L ...

  4. 倍增 Tarjan 求LCA

                                                                                                         ...

  5. 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 ...

  6. tarjan求lca的神奇

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  7. Tarjan求LCA(离线)

    基本思想 把要求的点对保存下来,在dfs时顺带求出来. 方法 将每个已经遍历的点指向它回溯的最高节点(遍历它的子树时指向自己),每遍历到一个点就处理它存在的询问如果另一个点已经遍历,则lca就是另一个 ...

  8. 图论分支-倍增Tarjan求LCA

    LCA,最近公共祖先,这是树上最常用的算法之一,因为它可以求距离,也可以求路径等等 LCA有两种写法,一种是倍增思想,另一种是Tarjan求法,我们可以通过一道题来看一看, 题目描述 欢乐岛上有个非常 ...

  9. Tarjan求LCA总结

    Tarjan算法向上标记法:从x向上走到根节点,并标记所有经过的点从y向上走到根节点,当第一次遇到已标记的节点时,就找到了LCA(x, y)对于每个询问,向上标记法的时间复杂度最坏为O(n) 在深度遍 ...

随机推荐

  1. 在VS2010上安装MVC4(webApi)

    我们安装的VS2010上是没有MVC4或者WebApi的,要想加入这些功能只能自己在网上下载安装. 要安装MVC4,首先得安装VS10sp(Service Package)1,然后再安装MVC4.安装 ...

  2. ubantu安全卸载火狐浏览器

    首先查看浏览器相应的安装包,命令: dpkg --get-selections |grep firefox 2.删除相面出现的包的命令: sudo apt-get purge pck1-name  p ...

  3. 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 ...

  4. spring集成rabbitmq

    https://www.cnblogs.com/nizuimeiabc1/p/9608763.html

  5. 有关svn的报错

    由于目标计算机积极拒绝,无法连接.当报出这样的错的时候就是跨域的问题

  6. IP欺骗

    通过一番测试,我发现当我连续提交3份问卷,再换一个IP提交3个问卷,也就是连续提交了6份问卷,并没有触发网站的反爬虫机制.所以我们可以猜测对方基于IP提交问卷的频率来识别爬虫程序的.看到这里,大家可能 ...

  7. ES6 promise学习

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大. 1.promise是一构造函数,既然是构造函数,那么我们就可以用 new Promise()得到一个p ...

  8. vue菜鸟从业记:完成项目最后一公里之真机测试和打包上线

    最近我朋友王小闰他们公司的项目开发已经进入收尾阶段,前后端并行开发的差不多了,联调也调过了,上篇文章里也讲到了,所谓联调,就仿佛在说“我也不知道我的接口文档写的对不对,我们验证一下吧?我也不知道我的数 ...

  9. 需要优化代码的leetcode

    1  关于验证字符串的问题: 2

  10. kubernetes in action - Pods

    Pods 在上一篇也说明了,pods是kubernetes的最小部署单元,并且所有在pods中的container共享namespaces 那么为什么需要pods这样的概念? 因为在实际中,我们有一种 ...