• 最近公共祖先~讲解


    最近公共祖先(Lowest Common Ancestors):

    简称LCA(并不是某轻型战斗机)

    一.何为最近公共祖先

    对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。(摘自百度百科)

    那么举个简单的例子:看图

    那么比如14和18的lca就是3   11和16的lca就是5

    对于求lca,有很多种方法:tarjan啦,树剖啦,倍增啦,暴力......

    二.如何求最近公共祖先呢

    首先我们想到了:暴力大法好

    对于所求的两个点,调整到同一个深度,然后一起向上跳,这样所求到的第一个相同的点就是这两个点的lca。

    可是暴力肯定会T...那么怎么办呢

    我们想到了之前学过的st表,利用倍增的思想来完成lca的查询。

    接着我们想到了:倍增大法好

    1.首先我们准备好要用到的变量

    所有点的2^j层的父亲,用fa数组保存,对于fa[i][j]表示编号为i的点向上跳2^j步的父亲。maxlog用来枚举j。

    head,edge,cnt邻接表的标配,不用多说。deep[i]表示i的深度,root为所给定的根节点编号。add为向树中添加一条边。

     1 const int maxlog = 20;
     2 const int maxn = 550000;
     3 int n, m, s;
     4 int root;
     5 int fa[maxn][maxlog];
     6 int deep[maxn];
     7 int head[maxn];
     8 int cnt;
     9 struct Edge{
    10     int next;
    11     int to;
    12 }e[2*maxn];
    13 void add(int u, int v)
    14 {
    15     e[cnt].to = v;
    16     e[cnt].next = head[u];
    17     head[u] = cnt++;
    18 }

    2.初始化建树,预处理每个点的fa数组

    dfs建树,ini初始化fa数组。

     1 void dfs(int u, int p, int d)
     2 {
     3     fa[u][0] = p;
     4     deep[u] = d;
     5     for(int i = head[u]; i != -1; i = e[i].next)
     6         if(e[i].to != p) dfs(e[i].to, u, d+1);
     7 }
     8 void init()
     9 {
    10     dfs (root, -1, 0);
    11     for(int k = 0; k + 1 < maxlog; k++)
    12     {
    13         for(int v = 1; v <= n; v++)
    14         if(fa[v][k] < 0) fa[v][k+1] = -1;
    15         else fa[v][k+1] = fa[fa[v][k]][k];
    16     }
    17 }

    3.接下来就是关于倍增lca的精髓所在:

    (划重点!)我们对于每一个点,还是先移动到同一高度,再开始跳。

    对于第一个for便是移动到相同深度。

    对于两个已经跳到了相同深度的两个点,我们从最高处向下跳,直到遇见一个两个点fa都不相同的点,即他们的lca下面的两个点。再求出这个两个点的向上跳一步的父节点。即lca。

     1 int lca(int u, int v)
     2 {
     3     if(deep[u] > deep[v]) swap(u, v);
     4     for(int k = 0; k < maxlog; k++)
     5     {
     6         if(deep[v] == deep[u]) break;
     7         if((deep[v] - deep[u]) >> k & 1) 
     8         {
     9         v = fa[v][k];
    10         //cout<<deep[v]<<" "<<deep[u]<<endl;    
    11         }//向上跳到同样深度 
    12     }
    13     
    14     if(u == v) return u;
    15 
    16     for(int k = maxlog - 1; k >= 0; k--)
    17     {
    18         if(fa[v][k] != fa[u][k])
    19         {
    20             u = fa[u][k];
    21             v = fa[v][k];
    22             //cout<<u<<" "<<v<<endl; 
    23         }
    24         
    25     }//从大向小跳下去 , 第一个遇到的不同的 
    26     return fa[u][0];
    27 }

    模板:

    https://www.luogu.org/problemnew/show/P3379

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <algorithm>
     4 #include <cstring>
     5 using namespace std;
     6 const int maxlog = 20;
     7 const int maxn = 550000;
     8 int n, m, s;
     9 int root;
    10 int fa[maxn][maxlog];
    11 int deep[maxn];
    12 int head[maxn];
    13 int cnt;
    14 struct Edge{
    15     int next;
    16     int to;
    17 }e[2*maxn];
    18 void add(int u, int v)
    19 {
    20     e[cnt].to = v;
    21     e[cnt].next = head[u];
    22     head[u] = cnt++;
    23 }
    24 void dfs(int u, int p, int d)
    25 {
    26     fa[u][0] = p;
    27     deep[u] = d;
    28     for(int i = head[u]; i != -1; i = e[i].next)
    29         if(e[i].to != p) dfs(e[i].to, u, d+1);
    30 }
    31 void init()
    32 {
    33     dfs (root, -1, 0);
    34     for(int k = 0; k + 1 < maxlog; k++)
    35     {
    36         for(int v = 1; v <= n; v++)
    37         if(fa[v][k] < 0) fa[v][k+1] = -1;
    38         else fa[v][k+1] = fa[fa[v][k]][k];
    39     }
    40 }
    41 int lca(int u, int v)
    42 {
    43     if(deep[u] > deep[v]) swap(u, v);
    44     for(int k = 0; k < maxlog; k++)
    45     {
    46         if(deep[v] == deep[u]) break;
    47         if((deep[v] - deep[u]) >> k & 1)
    48         {
    49         v = fa[v][k];
    50         //cout<<deep[v]<<" "<<deep[u]<<endl;    
    51         }
    52     }
    53     
    54     if(u == v) return u;
    55 
    56     for(int k = maxlog - 1; k >= 0; k--)
    57     {
    58         if(fa[v][k] != fa[u][k])
    59         {
    60             u = fa[u][k];
    61             v = fa[v][k];
    62         }
    63     }
    64     return fa[u][0];
    65 }
    66 int main()
    67 {
    68     memset(head,-1,sizeof(head)); 
    69     int a,b;
    70     scanf("%d%d%d",&n,&m,&root);
    71     for(int i = 1; i < n; i++)
    72     {
    73         scanf("%d%d",&a,&b);
    74         add(a,b);
    75         add(b,a);
    76     }
    77     init();
    78     for(int i = 1; i <= m; i++)
    79     {
    80         int u,v,a;
    81         scanf("%d%d",&u,&v);
    82         a = lca(u,v);
    83         printf("%d
    ",a);
    84     }
    85     return 0;    
    86 }

    隐约雷鸣,阴霾天空,但盼风雨来,能留你在此。

    隐约雷鸣,阴霾天空,即使天无雨,我亦留此地。

  • 相关阅读:
    excel文件导入mysql
    linux进程后台运行,且关终端后继续运行
    安装windows后grub修复
    华中科技大学 ubuntu14.04源
    Windows8.1远程桌面时提示凭据不工作的解决方案
    引文分析工具HistCite使用简介
    报账单打印
    iOS开发之--复制粘贴功能
    iOS学习之--字符串的删除替换(字符串的常用处理,删除,替换)
    iOS 裁剪View指定的角裁剪
  • 原文地址:https://www.cnblogs.com/MisakaAzusa/p/8612942.html
Copyright © 2020-2023  润新知