LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。 (来自百度百科)


一.倍增求LCA

预处理出距点u距离为2^0,2^1,2^2...的点作为f[u][0],f[u][1],f[u][2]...

d[u]记录点u的深度

用二进制拆分的思想,让更深的点向上跳,直至与另一个点深度相同

此时,如果两个点重合了,那么那个浅的点本身就是最近公共祖先

若两个点没有重合,则继续按二进制拆分的思想,两个点同时跳相同高度,跳过了就跳短一些,直至两个点重合

 #include<cstdio>
#include<iostream>
#include<cmath>
#define R register int
using namespace std;
const int N=;
inline int g() {
R ret=,fix=; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-:fix;
do ret=ret*+(ch^); while(isdigit(ch=getchar())); return ret*fix;
}
struct node{
int v,nxt;
#define v(i) e[i].v
#define nxt(i) e[i].nxt
}e[N<<];
int n,m,s,cnt;
int fir[N],d[N],f[N][];
inline void add(int u,int v) {v(++cnt)=v,nxt(cnt)=fir[u],fir[u]=cnt;}
inline void dfs(int u) {
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(d[v]) continue;
d[v]=d[u]+; f[v][]=u; R p=u;
for(R j=;f[p][j];++j) f[v][j+]=f[p][j],p=f[p][j];
dfs(v);
}
}
inline int ask(int u,int v) {
if(d[v]>d[u]) swap(u,v);
R lim=log2(d[u])+;
for(R i=lim;i>=;--i) if(d[f[u][i]]>=d[v]) u=f[u][i];
if(u==v) return u;
for(R i=lim;i>=;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][];
}
signed main() {
n=g(),m=g(),s=g();//n为结点数,m为询问次数,s为根节点
for(R i=,u,v;i<n;++i) u=g(),v=g(),add(u,v),add(v,u);
d[s]=; dfs(s);
for(R i=;i<=m;++i) {
R u=g(),v=g();
printf("%d\n",ask(u,v));
}
}

二.tarjan求LCA

Tarjan是的离线的

从根结点开始dfs,对遍历到的结点u标记已访问,创建新集合,元素为u,再遍历u的每一个子节点v,回溯时将每个子节点v的集合并到u的集合上,用并查集记录集合中的每个元素的fa为u,接着处理询问,对于关于u的每一个询问,若另一个结点v已访问,则可断定LCA(u,v)为fa[v],记录结果,最后按顺序输出即可

 #include<cstdio>
#include<iostream>
#define R register int
const int N=;
using namespace std;
inline int g() {
R ret=,fix=; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-:fix;
do ret=ret*+(ch^); while(isdigit(ch=getchar())); return ret*fix;
}
struct node{
int v,nxt;
#define v(i) e[i].v
#define nxt(i) e[i].nxt
#define vv(i) q[i].v
#define nn(i) q[i].nxt
}e[N<<],q[N<<];
int n,m,s,cnt,cntq;
int fir[N],fq[N],lca[N],fa[N];
bool vis[N],vq[N];
inline void add(int u,int v) {v(++cnt)=v,nxt(cnt)=fir[u],fir[u]=cnt;}
inline void addq(int u,int v) {vv(++cntq)=v,nn(cntq)=fq[u],fq[u]=cntq;}
inline int getf(int x) {return x==fa[x]?x:fa[x]=getf(fa[x]);}
inline void tarjan(int u) {
vis[u]=true;
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(vis[v]) continue;
tarjan(v);
fa[getf(v)]=u;
}
for(R i=fq[u];i;i=nn(i)) {
R v=vv(i);
if(vis[v]&&!vq[(i+)>>]) lca[(i+)>>]=getf(v),vq[(i+)>>]=true;
}
}
signed main(){
n=g(),m=g(),s=g();
for(R i=;i<=n;++i) fa[i]=i;
for(R i=,u,v;i<n;++i) u=g(),v=g(),add(u,v),add(v,u);
for(R i=,u,v;i<=m;++i) u=g(),v=g(),addq(u,v),addq(v,u);
tarjan(s);
for(R i=;i<=m;++i) printf("%d\n",lca[i]);
}

2019.04.02

浅谈最近公共祖先(LCA)的更多相关文章

  1. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

  2. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

  3. POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)

    POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...

  4. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  5. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

  6. 最近公共祖先 LCA 倍增算法

          树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...

  7. 最近公共祖先 lca (施工ing)

    声明 咳咳,进入重难点的图论算法之一(敲黑板): 题目: 洛谷 P3379 先放标程,施工ing,以后补坑!!!(实在太难,一个模板这么长 [ 不过好像还是没有 AC自动机 长哎 ],注释都打半天,思 ...

  8. 求最近公共祖先(LCA)的各种算法

    水一发题解. 我只是想存一下树剖LCA的代码...... 以洛谷上的这个模板为例:P3379 [模板]最近公共祖先(LCA) 1.朴素LCA 就像做模拟题一样,先dfs找到基本信息:每个节点的父亲.深 ...

  9. 与图论的邂逅05:最近公共祖先LCA

    什么是LCA? 祖先链 对于一棵树T,若它的根节点是r,对于任意一个树上的节点x,从r走到x的路径是唯一的(显然),那么这条路径上的点都是并且只有这些点是x的祖先.这些点组成的链(或者说路径)就是x的 ...

随机推荐

  1. html5--1.11列表

    html5--1.11列表 学习要点: 无序列表 有序列表 列表的属性 自定义列表 1.无序列表的基本格式 ul(unorder line)标签里面放li标签就好了,每一项就是一个li(LineIte ...

  2. java 基础 - 查找某个字串出现的次数及位置

    查找某个字串出现的次数及位置 public class search { public static void main(String[] args){ String str = "abc1 ...

  3. 1068 Bash游戏 V3

    1068 Bash游戏 V3 题目来源: Ural 1180 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题  收藏  关注 有一堆石子共有N个.A B两个人轮流拿 ...

  4. 【Lintcode】033.N-Queens II

    题目: Follow up for N-Queens problem. Now, instead outputting board configurations, return the total n ...

  5. Wmware Player中Linux挂载U盘

    菜单(Player)中有一项是可移动设备,中选择U盘,然后选择连接(断开主机), 然后在命令行中敲入 fdisk -l 正常情况下是sda是硬盘的信息,然后将会看到一个单蹦的sdb4的信息(sdb4可 ...

  6. poj2777Count Color——线段树+状压

    题目:http://poj.org/problem?id=2777 状压每个颜色的选择情况,取答案时 | 一番: 注意题目中的区间端点可能大小相反,在读入时换一下位置: 注意pushdown()中要l ...

  7. python script

    1.tab键自动补全(每次导入时要将脚本的路径加入到sys.path中) import sysimport readlineimport rlcompleterimport atexitimport ...

  8. 事务之六:spring 嵌套事务

    一.基本概念 事务的隔离级别,事务传播行为见<事务之二:spring事务(事务管理方式,事务5隔离级别,7个事务传播行为,spring事务回滚条件) > 二. 嵌套事务示例 2.1.Pro ...

  9. java 基础知识学习 JVM虚拟机参数配置

    1) 设置-Xms.-Xmx相等: 2) 设置NewSize.MaxNewSize相等: 3) 设置Heap size, PermGen space: Tomcat 的配置示例:修改%TOMCAT_H ...

  10. 6 git 生成SSH公钥/私钥 查看公钥

    如果没有公钥的话就生成公钥私钥:  $ ssh-keygen 然后连续回车(一次是位置,两次密码)