Live2D

LCA(最近公共祖先)暴力转倍增算法

今天,南外夏令营第一天打卡 , 咳咳,

LCA(最近公共祖先)

概念:为了便于了解,如图(在一颗有根树中,有若干个子树):

  其中1号节点为该树的根,我们所谓的祖先即一个子树的根及其的根的根和其的根的根的根……(在LCA中包括子树本身)(其实不用那么绕口),举个例子(在本文之后直接用编号来称呼节点):

5的祖先:5、2、1;7的祖先:7、3、1;2的祖先:2、1……

  而所谓的最近公共祖先为两个节点首先能找到的公共祖先(即深度最最大的公共祖先

 

  来个模版题()

下面就是激动人心的代码时刻让我们进行代码实现的步骤:

  1、先从最简单的暴搜入手:通过链表(从根一直往下搜,标记父节点)

       大概框架(时间复杂度nlogn

struct st{
    int to,next;
}edge[];
int hd[];
void add(int from,int to)
{
    cnt++;
    edge[cnt].next=hd[from];
    edge[cnt].to=to;
    hd[from]=cnt;
}
void DFS(int now,int d)找祖先
{
    deep[now]=d;
    for (int i=hd[now];i;i=edge[i].next)
    {
        fa[edge[i].to]=now;
        DFS(edge[i].to,d+1);
    }
}
void LCA()
{
    if (deep[x]<deep[y])swap(x,y);
    while (deep[x]>deep[y])x=fa[x];
    while (x!=y)
    {
        x=fa[x];
        y=fa[y];
    }
    ans=x;//或ans=y
}
暴搜

  2、倍增算法(时间复杂度logn

  首先,我们需要一定的二进制的基础:

  结论1:二进制的所有位只存在0或1,

       结论2:把十进制转二进制:10(10)-->1010(2),所以我们发现:一个十进制非负整数可以一个二进制数,(即可理解为一些2的次方相加!(这些次方一定不同,因为结论1得))

  结论3:2j-1+2j-1=2j

       运用到这里来:设f[i][j]:i号节点的第2j代的祖先编号,因为2j-1+2j-1=2j,所以一个节点的2j代的祖先是其2j-1代的祖先的2j-1的祖先得f[i][j]=f[f[i][j-1]][j-1]

      把暴力转倍增:

   1、把x,y转同深度(因为deep[x]的ans代祖先==deep[y]),ans转成一些2的次方相加:

for (int i=maxn;i>=0;i--)//必须从小到大:因为5=22+20,而如果从小到大:20+21=3,但3+22>5,不能"悔棋"!!!
{
     if (deep[f[x][i]]>=deep[y])x=f[x][i];
}

  2、找共同祖先:

    这里和上面的差不多:当前两个节点的第N代祖先是共同祖先,那他们的第N+x(x为非负整数)代祖先一定共同祖先

    为了防止越过N代,所以我们求第N-1代是什么,再求其的父辈!

for (int i=maxn;i>=0;i--)//必须从小到大,同理
    {
        if (f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
return f[x][0];

      这就大功告成!!

struct st{
    int to,next;
}edge[];
int hd[],cnt,deep[],f[][],ans,n,m,s;
int Find(int t)
{
    int u=1;
    while ((u<<1)<=t)u<<=1;
}
void add(int from,int to)
{
    cnt++;
    edge[cnt].next=hd[from];
    edge[cnt].to=to;
    hd[from]=cnt;
}
void DFS(int now,int d)
{
    deep[now]=d;
    int maxn=Find(d);
    for (int i=1;i<=maxn;i++)
    {
        f[now][i]=f[f[now][i-1]][i-1];
    }
    for (int i=hd[now];i;i=edge[i].next)
    {
            f[edge[i].to][0]=now;
            DFS(edge[i].to,d+1);
    }
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y])swap(x,y);
    int maxn=Find(x);
    for (int i=maxn;i>=0;i--)//必须从小到大
    {
        if (deep[f[x][i]]>=deep[y])x=f[x][i];
    }
    if (x==y)return x;
    maxn=Find(y);
    for (int i=maxn;i>=0;i--)//必须从小到大
    {
        if (f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}//仅供参考
参考
posted @ 2019-07-15 08:32  GDFS均均  阅读(273)  评论(0编辑  收藏  举报