poj1330 lca 最近公共祖先问题学习笔记

首先推荐两个博客网址:

http://dongxicheng.org/structure/lca-rmq/

http://scturtle.is-programmer.com/posts/30055.html

[转]tarjan算法的步骤是(当dfs到节点u时):
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最近的点

为什么要用祖先而且每次合并集合后都要确保集合的祖先正确呢?
因为集合是用并查集实现的,为了提高速度,当然要平衡加路径压缩了,所以合并后谁是根就不确定了,所以要始终保持集合的根的祖先是正确的
关于查询和遍历孩子的顺序:
wikipedia上就是上文中的顺序,很多人的代码也是这个顺序
但是网上的很多讲解却是查询在前,遍历孩子在后,对比上文,会不会漏掉u和u的子孙之间的查询呢?
不会的
如果在刚dfs到u的时候就设置u为visited的话,本该回溯到u时解决的那些查询,在遍历孩子时就会解决掉了
这个顺序问题就是导致我头大看了很久这个算法的原因,也是絮絮叨叨写了本文的原因,希望没有理解错= =

对于这道题

题意:求最近公共祖先lca

下面是学来的tarjan代码

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <cmath>
  7 #include <ctime>
  8
  9 using namespace std;
 10
 11 class Edge
 12 {
 13     public:
 14     int    to;
 15     int    next;
 16 }e[11000];
 17
 18 int    n,T,cnt;
 19 int    f[11000],depth[11000],anc[11000],p[11000];
 20 bool    visited[11000],In[11000];
 21
 22 inline    void    Add_edge(const int & x,const int & y)
 23 {
 24     e[++cnt].to=y;
 25     e[cnt].next=p[x];
 26     p[x]=cnt;
 27     return ;
 28 }
 29
 30 inline    int    get_anc(const int &x)
 31 {
 32     return f[x]==x ? x:f[x]=get_anc(f[x]);
 33 }
 34
 35 inline    void    Union(const int & x,const int & y)
 36 {
 37     int    S,T;
 38     S=get_anc(x);
 39     T=get_anc(y);
 40     if(S==T)return ;
 41     if(depth[S]<=depth[T])
 42         f[S]=T,depth[S]+=depth[T];
 43     else
 44         f[T]=S,depth[T]+=depth[S];
 45     return ;
 46 }
 47
 48 void    Dfs(const int &S,const int d)
 49 {
 50     int    i;
 51     depth[S]=d;
 52     for(i=p[S];i;i=e[i].next)
 53     {
 54         Dfs(e[i].to,d+1);
 55     }
 56
 57     return ;
 58 }
 59
 60 int    Lca_tarjan(const int & s,const int & t,const int & u)
 61 {
 62     int    i,temp;
 63
 64     anc[u]=u;
 65     for(i=p[u];i;i=e[i].next)
 66     {
 67         temp=Lca_tarjan(s,t,e[i].to);
 68         if(temp)return temp;
 69         Union(u,e[i].to);
 70         anc[get_anc(u)]=u;
 71     }
 72
 73     visited[u]=true;
 74     if(s==u&&visited[t])
 75         return anc[get_anc(t)];
 76     if(t==u&&visited[s])
 77         return anc[get_anc(s)];
 78
 79     return 0;
 80 }
 81
 82 inline    void    Init()
 83 {
 84     cnt=0;
 85     memset(depth,0,sizeof(depth));
 86     memset(visited,0,sizeof(visited));
 87     memset(anc,0,sizeof(anc));
 88     memset(p,0,sizeof(p));
 89     memset(e,0,sizeof(e));
 90     memset(In,0,sizeof(In));
 91
 92     for(int i=1;i<=n;++i)
 93     {
 94         f[i]=i;
 95     }
 96
 97     return ;
 98 }
 99
100 int main()
101 {
102     //freopen("1330.in","r",stdin);
103
104     int    i,x,y,s,t,S;
105
106     scanf("%d",&T);
107     while(T--)
108     {
109         scanf("%d",&n);
110         Init();
111         for(i=1;i<n;++i)
112         {
113             scanf("%d%d",&x,&y);
114             Add_edge(x,y);
115             In[y]=true;
116         }
117
118         for(S=1;S<=n;++S)
119             if(!In[S])break;
120
121         scanf("%d%d",&s,&t);
122         Dfs(S,1);
123         printf("%d\n",Lca_tarjan(s,t,S));
124     }
125
126     return 0;
127 }
时间: 2024-10-12 12:59:04

poj1330 lca 最近公共祖先问题学习笔记的相关文章

笔记: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 最近公共祖先

http://poj.org/problem?id=1330 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 const int inf=0x3f3f3f3f; 7 class LCA { ///最近公共祖先 build_o(n*logn) query_o(1) 8 t

Codeforces Round #362 (Div. 2) C. Lorenzo Von Matterhorn LCA(最近公共祖先)

C. Lorenzo Von Matterhorn time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Barney lives in NYC. NYC has infinite number of intersections numbered with positive integers starting from 1. Ther

POJ 1330 LCA最近公共祖先 离线tarjan算法

题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集和dfs染色,先dfs到底层开始往上回溯,边并查集合并 一边染色,这样只要询问的两个点均被染色了,就可以输出当前并查集的最高父亲一定是LCA,因为我是从底层层层往上DSU和染色的,要么没被染色,被染色之后,肯定就是当前节点是最近的 #include <iostream> #include <

POJ-1330&amp;HDU-2586 最近公共祖先(不带权+带权)树剖式写法求LCA

其实敲树剖敲多了就会手敲,然后就发现其实树剖也是可以求LCA的,根据树剖的经验,我们两遍dfs后关于询问l,r的情况我们就开始跳链,当l,r处于同一个链的时候返回深度较小的那个点就好了,这里给个例题: 题目链接:http://poj.org/problem?id=1330 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown

LCA最近公共祖先(POJ1330)

题目链接:http://poj.org/problem?id=1330 解题报告: 先将一个子节点,深搜每一个根节点,并标记. 然后深索另一个子节点,当发现访问过了,就找到了最近的公共祖先. #include <iostream> #include <stdio.h> #include <string.h> using namespace std; const int maxn = 10005; bool vis[maxn]; int father[maxn]; int

连通分量模板:tarjan: 求割点 &amp;&amp; 桥 &amp;&amp; 缩点 &amp;&amp; 强连通分量 &amp;&amp; 双连通分量 &amp;&amp; LCA(最近公共祖先)

PS:摘自一不知名的来自大神. 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合. 3.点连通度:最小割点集合中的顶点数. 4.割边(桥):删掉它之后,图必然会分裂为两个或两个以上的子图. 5.割边集合:如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合. 6.边连通度:一个图的边连通度的定义为,最

[转]LCA 最近公共祖先

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

(转)Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)

本文转载自:http://hi.baidu.com/lydrainbowcat/item/f8a5ac223e092b52c28d591c 作者提示:在阅读本文之前,请确保您已经理解并掌握了基本的Tarjan算法,不会的请到http://hi.baidu.com/lydrainbowcat/blog/item/42a6862489c98820c89559f3.html阅读.   基本概念:   1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如