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 }
  • 相关阅读:
    Java 之 Maven 基础
    JavaScript 之 RegExp 对象
    Java 之 Jedis
    Java 之 Redis 基础
    Java 之 NOSQL
    JavaWeb 之 JSON
    JavaWeb 之 Ajax
    【LeetCode-数组】外观数列
    【LeetCode-树】从先序遍历还原二叉树
    【LeetCode-数组】搜索二维矩阵 II
  • 原文地址:https://www.cnblogs.com/JinLeiBo/p/9398338.html
Copyright © 2011-2022 走看看