浅谈最近公共祖先(LCA)


 

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的深度

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

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

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

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #define R register int
 5 using namespace std;
 6 const int N=500010;
 7 inline int g() {
 8     R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
 9     do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
10 }
11 struct node{
12     int v,nxt;
13     #define v(i) e[i].v
14     #define nxt(i) e[i].nxt
15 }e[N<<1];
16 int n,m,s,cnt;
17 int fir[N],d[N],f[N][20];
18 inline void add(int u,int v) {v(++cnt)=v,nxt(cnt)=fir[u],fir[u]=cnt;}
19 inline void dfs(int u) {
20     for(R i=fir[u];i;i=nxt(i)) {
21         R v=v(i);
22         if(d[v]) continue;
23         d[v]=d[u]+1; f[v][0]=u; R p=u;
24         for(R j=0;f[p][j];++j) f[v][j+1]=f[p][j],p=f[p][j];
25         dfs(v);
26     }
27 }
28 inline int ask(int u,int v) {
29     if(d[v]>d[u]) swap(u,v);
30     R lim=log2(d[u])+1;
31     for(R i=lim;i>=0;--i) if(d[f[u][i]]>=d[v]) u=f[u][i];
32     if(u==v) return u;
33     for(R i=lim;i>=0;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
34     return f[u][0];
35 }
36 signed main() {
37     n=g(),m=g(),s=g();//n为结点数,m为询问次数,s为根节点
38     for(R i=1,u,v;i<n;++i) u=g(),v=g(),add(u,v),add(v,u);
39     d[s]=1; dfs(s);
40     for(R i=1;i<=m;++i) {
41         R u=g(),v=g();
42         printf("%d\n",ask(u,v));
43     }
44 }

 

二.tarjan求LCA 

Tarjan是的离线的

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

 1 #include<cstdio>
 2 #include<iostream>
 3 #define R register int
 4 const int N=500010;
 5 using namespace std;
 6 inline int g() {
 7     R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
 8     do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
 9 }
10 struct node{
11     int v,nxt;
12     #define v(i) e[i].v
13     #define nxt(i) e[i].nxt
14     #define vv(i) q[i].v
15     #define nn(i) q[i].nxt
16 }e[N<<1],q[N<<1];
17 int n,m,s,cnt,cntq;
18 int fir[N],fq[N],lca[N],fa[N];
19 bool vis[N],vq[N];
20 inline void add(int u,int v) {v(++cnt)=v,nxt(cnt)=fir[u],fir[u]=cnt;}
21 inline void addq(int u,int v) {vv(++cntq)=v,nn(cntq)=fq[u],fq[u]=cntq;}
22 inline int getf(int x) {return x==fa[x]?x:fa[x]=getf(fa[x]);}
23 inline void tarjan(int u) {
24     vis[u]=true;
25     for(R i=fir[u];i;i=nxt(i)) {
26         R v=v(i);
27         if(vis[v]) continue;
28         tarjan(v);
29         fa[getf(v)]=u;
30     }
31     for(R i=fq[u];i;i=nn(i)) {
32         R v=vv(i);
33         if(vis[v]&&!vq[(i+1)>>1]) lca[(i+1)>>1]=getf(v),vq[(i+1)>>1]=true;
34     }
35 }
36 signed main(){
37     n=g(),m=g(),s=g();
38     for(R i=1;i<=n;++i) fa[i]=i;
39     for(R i=1,u,v;i<n;++i) u=g(),v=g(),add(u,v),add(v,u);
40     for(R i=1,u,v;i<=m;++i) u=g(),v=g(),addq(u,v),addq(v,u);
41     tarjan(s);
42     for(R i=1;i<=m;++i) printf("%d\n",lca[i]);
43 }

2019.04.02

 

posted @ 2019-04-02 21:51  LuitaryiJack  阅读(292)  评论(1编辑  收藏  举报