Tarjan--LCA算法的个人理解即模板

tarjan---LCA算法的步骤是(当dfs到节点u时):

实际:  并查集+dfs

具体步骤:
1 在并查集中建立仅有u的集合,设置该集合的祖先为u
1 对u的每个孩子v:
   1.1 tarjan之
   1.2 合并v到父节点u的集合,确保集合的祖先是u
2 设置u为已遍历
3 处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)=v所在的集合的祖先。

举例子:

假设遍历完10的孩子,要处理关于10的请求了
取根节点到当前正在遍历的节点的路径为关键路径,即1-3-8-10
集合的祖先便是关键路径上距离集合最近的点
比如此时:
    1,2,5,6为一个集合,祖先为1,集合中点和10的LCA为1
    3,7为一个集合,祖先为3,集合中点和10的LCA为3
    8,9,11为一个集合,祖先为8,集合中点和10的LCA为8
    10,12为一个集合,祖先为10,集合中点和10的LCA为10
你看,集合的祖先便是LCA吧,所以第3步是正确的
道理很简单,LCA(u,v)便是根至u的路径上到节点v最近的点

此段话语来自sre="http://purety.jp/akisame/oi/TJU/"

模板:

  1 #include<iostream>
  2 #include<vector>
  3 using namespace std;
  4
  5 const int MAX=10001;
  6 int father[MAX];
  7 int rank[MAX];
  8 int indegree[MAX];//保存每个节点的入度
  9 int visit[MAX];
 10 vector<int> tree[MAX],Qes[MAX];
 11 int ancestor[MAX];
 12
 13
 14 void init(int n)
 15 {
 16     for(int i=1;i<=n;i++)
 17     {
 18
 19         rank[i]=1;
 20         father[i]=i;
 21         indegree[i]=0;
 22         visit[i]=0;
 23         ancestor[i]=0;
 24         tree[i].clear();
 25         Qes[i].clear();
 26     }
 27
 28 }
 29
 30 int find(int n)
 31 {
 32     if(father[n]==n)
 33         return n;
 34     else
 35         father[n]=find(father[n]);
 36     return father[n];
 37 }//查找函数,并压缩路径
 38
 39 int Union(int x,int y)
 40 {
 41     int a=find(x);
 42     int b=find(y);
 43     if(a==b)
 44         return 0;
 45     //相等的话,x向y合并
 46     else if(rank[a]<=rank[b])
 47     {
 48         father[a]=b;
 49         rank[b]+=rank[a];
 50     }
 51     else
 52     {
 53         father[b]=a;
 54         rank[a]+=rank[b];
 55     }
 56     return 1;
 57
 58 }//合并函数,如果属于同一分支则返回0,成功合并返回1
 59
 60
 61 void LCA(int u)
 62 {
 63     ancestor[u]=u;
 64     int size = tree[u].size();
 65     for(int i=0;i<size;i++)
 66     {
 67         LCA(tree[u][i]);
 68         Union(u,tree[u][i]);
 69         ancestor[find(u)]=u;
 70     }
 71     visit[u]=1;
 72     size = Qes[u].size();
 73     for(int i=0;i<size;i++)
 74     {
 75         //如果已经访问了问题节点,就可以返回结果了.
 76         if(visit[Qes[u][i]]==1)
 77         {
 78             cout<<ancestor[find(Qes[u][i])]<<endl;
 79             return;
 80         }
 81     }
 82 }
 83
 84
 85 int main()
 86 {
 87     int cnt;
 88     int n;
 89     cin>>cnt;
 90     while(cnt--)
 91     {
 92         cin>>n;;
 93         init(n);
 94         int s,t;
 95         for(int i=1;i<n;i++)
 96         {
 97             cin>>s>>t;
 98             tree[s].push_back(t);
 99             indegree[t]++;
100         }
101         //这里可以输入多组询问
102         cin>>s>>t;
103         //相当于询问两次
104         Qes[s].push_back(t);
105         Qes[t].push_back(s);
106         for(int i=1;i<=n;i++)
107         {
108             //寻找根节点
109             if(indegree[i]==0)
110             {
111                 LCA(i);
112                 break;
113             }
114         }
115     }
116     return 0;
117 }
时间: 2024-08-30 06:27:56

Tarjan--LCA算法的个人理解即模板的相关文章

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

1.前言 首先我们介绍的算法是LCA问题中的离线算法-Tarjan算法,该算法采用DFS+并查集,再看此算法之前首先你得知道并查集(尽管我相信你如果知道这个的话肯定是知道并查集的),Tarjan算法的优点在于相对稳定,时间复杂度也比较居中,也很容易理解(个人认为). 2.思想 下面详细介绍一下Tarjan算法的思想: 1.任选一个点为根节点,从根节点开始. 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过. 3.若是v还有子节点,返回2,否则下一步. 4.合并v到u上. 5.寻找与当前点

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(离线)算法

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

HDU 2586 How Far Away?(Tarjan离线算法求lca)

题意:给定一棵树n个节点m个询问,每次询问两个节点之间的距离. 思路:Tarjan离线算法求lca. 这题一开始交了n发一直爆栈.......百度了一下大概说的是这样hdu用的是windows服务器所以栈大小极其坑爹,稍微深一点的递归就会爆栈(正式比赛一般不会爆) 解决方法就是加一句#pragma comment(linker, "/STACK:1024000000,1024000000") 用c++交就好.....当然这只是针对比较坑爹oj来说的取巧的方法 #include<c

LCA 算法学习 (最近公共祖先)poj 1330

poj1330 在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点.这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共祖先只有一个.时间复杂度为O(n+q

Modified LCS (扩展欧几里德)细写了对这个算法思路的理解

题目:Modified LCS 为过此题去仔细研究了下扩展欧几里德算法,领悟了一些精华. 模板为: void gcd(ll a, ll b, ll& d, ll& x, ll& y) { if(!b) {d = a; x = 1; y = 0;} else{ gcd(b, a%b, d, y, x); y -= x*(a/b);} } 这里算出来的x,y是对于方程ax+by=gcd(a,b)而言的一组解. 为什么叫扩展欧几里德说明肯定用了欧几里德算法的原理即:gcd(a,b)=gc

hdu 5286 How far away ? tarjan/lca

How far away ? Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How f

hdu3078 建层次树+在线LCA算法+排序

题意:n个点,n-1条边构成无向树,每个节点有权,Q次询问,每次或问从a->b的最短路中,权第k大的值,/或者更新节点a的权, 思路:在线LCA,先dfs生成树0,标记出层数和fa[](每个节点的父亲节点).在对每次询问,走一遍一次公共祖先路上 的权,保持,快排.n*logn*q #include<iostream> //187MS #include<algorithm> #include<cstdio> #include<vector> using

递归,回溯,DFS,BFS的理解和模板【摘】

递归:就是出现这种情况的代码: (或者说是用到了栈) 解答树角度:在dfs遍历一棵解答树 优点:结构简洁缺点:效率低,可能栈溢出 递归的一般结构: 1 void f() { 2 if(符合边界条件) { 3 /////// 4 return; 5 } 6 7 //某种形式的调用 8 f(); 9 } 回溯:递归的一种,或者说是通过递归这种代码结构来实现回溯这个目的.回溯法可以被认为是一个有过剪枝的DFS过程.解答树角度:带回溯的dfs遍历一棵解答树回溯的一般结构: 1 void dfs(int