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. RQNOJ 329 刘翔!加油!:01背包

    题目链接:https://www.rqnoj.cn/problem/329 题意: 刘翔有n封信,每封信都有自己的欣赏价值value[i].消耗时间time[i].消耗体力h[i].和得到的鼓舞w[i ...

  2. ubuntu安装 LNMP+redis

    一.更新软件源 1.修改软件源为163的源 sudo vim /etc/apt/sources.list 替换源为163的源: deb http://mirrors.163.com/ubuntu/ i ...

  3. linux进程学习-进程描述符的存储

    当进程被新建时,内核会给进程分配一个8K的空间作为进程的内核堆栈.同时我们知道task_struct结构体也会被创建,但有意思的是,内核不会给task_struct单独分别空间,而是直接将其扔到8k的 ...

  4. FileHeader 详解文件头部注释-sublimeText

    FileHeader 前言: 直奔主题吧,sublimeText 的大部分插件都是非常好用,易用的,而且很容易找到资料.这次用 FileHeader 发现网上的资料很少,而且大部分说得都很模糊,或者直 ...

  5. 暑假集训Chapter1 贪心

    为什么要今天写呢? 明天全力研究Johnson和一些奇奇怪怪的贪心 今天把能写的都写了 T1T2太水了直接上代码吧 #include<bits/stdc++.h> #define LL l ...

  6. POJ3693Maximum repetition substring (循环节)(后缀数组+RMQ)

    The repetition number of a string is defined as the maximum number R such that the string can be par ...

  7. 「NOIP2006」「LuoguP1064」 金明的预算方案(分组背包

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行” ...

  8. bzoj 3267: KC采花&&3272&&3638&&3502 线段树

    题目大意 给定一个长为n的序列,维护两种操作: 1.单点修改 2.在[l,r]这段区间中取k个互不相交的子段,使子段之和最大. \(n \leq 50000,k \leq 20\) 题解 四倍经验.( ...

  9. 【Lintcode】105.Copy List with Random Pointer

    题目: A linked list is given such that each node contains an additional random pointer which could poi ...

  10. windows下vs2012用gsoap开发webservice实例

    零:说明 1.本文是根据网上前人经验结合自己动手操作写成,开发工具用的vs2012,gsoap用的是gsoap-2.8: 2.gsoap提供的工具简单介绍 1)wsdl2h.exe:根据WSDL文件生 ...