zoukankan      html  css  js  c++  java
  • 浅谈最近公共祖先(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
    ",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
    ",lca[i]);
    43 }

    2019.04.02

  • 相关阅读:
    排序算法
    顺序表与链表
    二叉树
    查找算法
    15 Django 离线脚本
    poj 1330 Nearest Common Ancestors(LCA 基于二分搜索+st&rmq的LCA)
    hdu 6158 The Designer( 反演圆)
    圆的反演性质
    HDU 6153 A Secret(扩展kmp)
    kmp&扩展kmp
  • 原文地址:https://www.cnblogs.com/Jackpei/p/10360550.html
Copyright © 2011-2022 走看看