zoukankan      html  css  js  c++  java
  • [LCA]倍增法

    LCA(least common ancestors)最近公共祖先

    指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先。

    定义到此。

    那么怎么求LCA?

    对于朴素思想,就是我要一步一步往上爬。。一步一步走。先把结点x和y整到同一深度,然后再一次一个深度的往上查,直到祖先一样才break(明显是个while)

    但是,一步一步实在是太慢了,所以不能脚踏实地地走

    那么,考虑跳着走,

    跳着走的条件就是要满足一步步数尽可能多并且不跳过了。当然你跳过了在一步一步往下找也行啊QWQ

    于是,在满足这两个条件的情况下,我们有了LCA算法:

    一步条2的n次方。

    对于每一步跳跃,我们要预处理一个二维数组f(father)f[x][i],表示对于当前的x结点,往上跳2^i个祖先,特别的,像数学中的,f[x][0]就是x的一代祖先。于是我们要用一个dfs预处理来解决这个f数组。后面我们会提到;

    处理完f数组,那就很简单了。

    输入x和y,表示要求结点x和结点y的LCA,那么我们开始求LCA:

    对于x和y在不同高度,我们先把他们拉到一个高度,同样不能一步一步走,也要用到f数组。

    这里我们要提前了解到一个定理:对于任意一个非零整数,我们都可以将他用2的次幂表示出来。也就是such as  : 11=2^3+2^1+2^0。这倒也不用证明,就像每个数都可以用二进制表示一样。

    接着讲:在把x和y弄到同一高度时,我们要先做的是设x的深度dep要比y的深度dep要大,如果dep[y]>dep[x],那么把他们交换。原因是如果不交换,那么我们需要判断两种情况,用两个if以及几乎相同的代码。。(乱七八糟还占内存)

    然后用一个判断    if(dep[f[x][i]]>=dep[y])x=f[x][i];(此时x深度大于y),在这里用外层for循环来枚举i,但是i一定要从大到小!。

    那么,类比于x和y的深度的距离,这个瓶子的容积也是同样道理。从2的尽可能大的次幂去找,一旦能找到能接近他们的i,就更新dep[x],直到相等。类似于无限逼近,最后值相等的过程。如果i相等了,就说明他们的深度已经在同一层了。

    与倍增到同一深度的过程相比,倍增找公共祖先也是类似的,但是有一点不同,就是你不知道什么时候找到他们的公共祖先,因此就没有查找的上限,那么就让他们尽可能接近。因此条件改成了这个:

    if(f[x][i]!=f[y][i])
            {
                x=f[x][i];y=f[y][i];
            }

    在执行完这个语句后,我们成功让x和y变成了某一节点z的左右儿子!

    因为左右儿子是最接近但是又不相等的。

    那么我们随便取其中一个找爸爸,就找到了LCA。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cmath>
     4 #include <algorithm>
     5 #include <set>
     6 #include <queue>
     7 #include <stack>
     8 #include <string>
     9 #include <cstring>
    10 #include <vector>
    11 #include <map>
    12 //#include <unordered_map>
    13 #define mem( a ,x ) memset( a , x ,sizeof(a) )
    14 #define rep( i ,x ,y ) for( int i = x ; i<=y ;i++ )
    15 #define lson  l ,mid ,pos<<1
    16 #define rson mid+1 ,r ,pos<<1|1
    17 using namespace std;
    18 typedef long long ll ;
    19 typedef pair<int ,int> pii;
    20 typedef pair<ll ,int> pli;
    21 const int inf = 0x3f3f3f3f;
    22 const ll mod=998244353;
    23 const int N=1e5+50;
    24 int n,xx,yy,m;
    25 int Link[5010],len=0,val[5010];
    26 int deep[5010];
    27 struct node
    28 {
    29     int y,next,id;
    30 }e[N];
    31 void insert(int xx,int yy,int id)
    32 {
    33     e[++len].next=Link[xx];
    34     Link[xx]=len;
    35     e[len].y=yy;
    36     e[len].id=id;
    37 }
    38 void dfs(int x,int fa)
    39 {
    40     deep[x]=deep[fa]+1;
    41     for(int i=1;(1<<i)<=deep[x];i++)
    42         f[x][i]=f[f[x][i-1]][i-1];
    43     for(int i=Link[x];i;i=e[i].next)
    44     {
    45         int v=e[i].y;
    46         if(v==fa)   continue;
    47         f[v][0]=x;
    48         dfs(v,x);
    49     }
    50 }
    51 int lca(int x,int y)
    52 {
    53     if(deep[x]<deep[y]) swap(x,y);
    54     for(int i=20;i>=0;i--)
    55     {
    56         if(deep[f[x][i]]>=deep[y])  x=f[x][i];
    57         if(x==y)    return x;
    58     }
    59     for(int i=20;i>=0;i--)
    60     {
    61         if(f[x][i]!=f[y][i])
    62         {
    63             x=f[x][i];
    64             y=f[y][i];
    65         }
    66     }
    67     return f[x][0];
    68 }
    69 int main()
    70 {
    71     scanf("%d",&n);
    72     for(int i=1;i<n;i++)
    73     {
    74         scanf("%d%d",&xx,&yy);
    75         insert(xx,yy,i);
    76         insert(yy,xx,i);
    77     }
    78 
    79     return 0;
    80 }
    View Code
  • 相关阅读:
    十四、oracle 数据库管理--管理表空间和数据文件
    十一、oracle 数据库管理员
    十二、oracle 数据库(表)的逻辑备份与恢复
    九、oracle 事务
    十、oracle 常用函数
    八、oracle 分页
    七、oracle 表查询二
    五、oracle 表的管理
    六、表查询一
    四、oracle 用户管理(Profile)
  • 原文地址:https://www.cnblogs.com/Kaike/p/12312896.html
Copyright © 2011-2022 走看看