zoukankan      html  css  js  c++  java
  • 倍增求LCA算法详解

    算法介绍:

    看到lca问题(不知道lca是什么自(bang)行(ni)百度),不难想到暴力的方法;

    先把两点处理到同一深度,再让两点一个一个祖先往上找,直到找到一个相同的祖先;

    这么暴力的话,时间复杂度基本上是$ o(n) $;

    而观察一下暴力的过程,就会发现,其实一个一个祖先往上找效率非常的低,有没有能优化这一过程的方法呢?这时,强大的倍增就出现了,能够把暴力优化到$ o(log(n)) $;

    倍增,简单说就是把一步一步跳替换成每次跳$ 2^i $个祖先;

    做法:

    先预处理出每个点的深度(dfs或bfs),以及跳$ 2^i $个祖先后所在的位置(fa[i][j]表示第i个点跳$ 2^j $个祖先后的位置,再递推);

    然后同理暴力,先把两点跳到同一深度(也用倍增),每次跳$ 2^i $个祖先,判断是否相等,如果相等就不跳(原因见易错点),否则跳;

    难点:

    1、递推时方程为fa[i][j]=fa[fa[i][j-1]][j-1],因为i跳$ 2^j $个祖先后所在的位置等于i连跳两次$ 2^{j-1} $个祖先后所在的位置;

    2、递推时,j先扫1到20,i再扫1到n,因为每次更新f[i][j]要用到另外的位置跳$ 2^{j-1} $个祖先后所在的位置;

    3、两个点跳的时候,如果相等,是不能直接输出的,有可能跳过头,就不是最近的公共祖先了;

    4、每次读进来的边要存两次(树是无向图),同理,邻接表的数组也要开两倍长;

    相关题目

    1、洛谷p3379

    模板题,放上丑陋的代码

     1 #include<cstdio>
     2 using namespace std;
     3 const int MAXN=1000001;//两倍长 
     4 int tot,n,m,s,first[MAXN],last[MAXN],next[MAXN],to[MAXN],depth[MAXN],fa[MAXN][21];
     5 //depth是每个点的深度,fa[i][j]表示第i个点跳2^j个祖先后的位置
     6 bool visited[MAXN];
     7 inline int read()//快读 
     8 {
     9    int s=0,w=1;
    10    char ch=getchar();
    11    while(ch<='0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    12    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    13    return s*w;
    14 }
    15 void swap(int &x,int &y)
    16 {
    17     int k=x;
    18     x=y;
    19     y=k;
    20 }
    21 void add(int x,int y)//古董邻接表 
    22 {
    23     ++tot;
    24     if(first[x]==0) first[x]=tot; else next[last[x]]=tot;
    25     last[x]=tot;
    26     to[tot]=y;
    27 }
    28 void dfs(int now,int dep,int fat)//深搜求深度 
    29 {
    30     if(visited[now]) return;
    31     visited[now]=true;
    32     depth[now]=dep;
    33     fa[now][0]=fat;//要记得fa[i][0]存i的父亲(跳一(2^0)个祖先) 
    34     for(int i=first[now];i;i=next[i])
    35     {
    36         dfs(to[i],dep+1,now);
    37     }
    38 }
    39 int lca(int x,int y)
    40 {
    41     if(depth[x]<depth[y]) swap(x,y);
    42     for(int i=20;i>=0;--i)
    43     if(fa[x][i]!=0&&depth[fa[x][i]]>=depth[y])
    44     {
    45         x=fa[x][i];
    46     }
    47     if(x==y) return x;
    48     for(int i=20;i>=0;--i)
    49     if(fa[x][i]!=0&&fa[y][i]!=0&&fa[x][i]!=fa[y][i])
    50     {
    51         x=fa[x][i];
    52         y=fa[y][i];
    53     }
    54     return fa[x][0];
    55 }
    56 int main()
    57 {
    58     n=read();
    59     m=read();
    60     s=read();
    61     for(int i=1;i<=n-1;++i)
    62     {
    63         int x,y;
    64         x=read();
    65         y=read();
    66         add(x,y);
    67         add(y,x);
    68     }
    69     dfs(s,1,0);
    70     for(int j=1;j<=20;++j)
    71         for(int i=1;i<=n;++i)
    72         fa[i][j]=fa[fa[i][j-1]][j-1];
    73     for(int i=1;i<=m;++i)
    74     {
    75         int x;
    76         int y;
    77         x=read();
    78         y=read();
    79         printf("%d
    ",lca(x,y));
    80     }
    81     return 0;
    82 }
  • 相关阅读:
    Android Gradle Plugin指南(五)——Build Variants(构建变种版本号)
    文件内容操作篇clearerr fclose fdopen feof fflush fgetc fgets fileno fopen fputc fputs fread freopen fseek ftell fwrite getc getchar gets
    文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write
    嵌入式linux应用程序调试方法
    version control system:git/hg/subversion/cvs/clearcase/vss。software configruation management。代码集成CI:Cruisecontrol/hudson/buildbot
    最值得你所关注的10个C语言开源项目
    如何记录linux终端下的操作日志
    CentOS 5.5 虚拟机安装 VirtualBox 客户端增强功能
    sizeof, strlen区别
    C/C++嵌入式开发面试题
  • 原文地址:https://www.cnblogs.com/JinLeiBo/p/9398338.html
Copyright © 2011-2022 走看看