LCA模板(Multiplication of trees)

题目:

模板题不用概括大意了吧
给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

解题思路:

树上倍增
fa[i,j]表示第i个点的2 j 级别的祖先
depth[x]表示x的深度
我们预先算出log_2(i)+1的值,这样每次调用就是O(1),如果用C语言的log会比较慢,自己推可以达到常数优化的效果
lg[i]=log_2(i)+1=lg[i-1]+(1<<lg[i-1]==i)
接着我们把比较深的点往上拉,拉到与另外一个点的高度相同再开始倍增。
倍增就是每次往上跳2的次幂个节点,我们不是1,2,4,8……这样跳,这样跳过了又要回溯一遍,如果我们64,32,16……这样跳就不用回溯
我们跳完后相同的并不是这两个点,而是它们的父亲,于是返回值是fa[x][0] or fa[y][0]

Accepted code:

#include<bits/stdc++.h>
#define N 500001
using namespace std;
struct node{
    int y,next;
}a[N<<1];
int last[N],depth[N],fa[N][25],lg[N],cnt;
int n,m,s;
inline void add(int x,int y)//建树
{
    a[++cnt].y=y;a[cnt].next=last[x];last[x]=cnt;
    a[++cnt].y=x;a[cnt].next=last[y];last[y]=cnt;
}
inline int read()
{
    int f=0,ag=1;char c=getchar();
    while(!isdigit(c)) {if (c=='-') ag=-1; c=getchar();}
    while(isdigit(c)){f=(f<<3)+(f<<1)+c-48;c=getchar();}
    return f*ag;
}
inline void CSH()
{
    for(int i=1;i<=n;i++)
      lg[i]=lg[i-1]+(1<<lg[i-1]==i);
}
void dfs(int now,int f)//now是当前点,f是上次搜的点
{
    depth[now]=depth[f]+1;//记录深度
    fa[now][0]=f;//记录父亲
    for(int i=1;(1<<i)<=depth[now];i++)
        fa[now][i]=fa[fa[now][i-1]][i-1];
    //就是爷爷是父亲的父亲,高祖父是爷爷的爷爷这个道理
    for (int i=last[now];i;i=a[i].next)
        if (a[i].y!=f)
            dfs(a[i].y,now);//往下搜
}
int LCA(int x,int y)
{
    if (depth[x]<depth[y])
        swap(x,y);//不妨设x的深度≥y的深度
    while(depth[x]>depth[y])
        x=fa[x][lg[depth[x]-depth[y]]-1];
    //把x拉到y的深度
    if (x==y) return x;//如果y是x的祖先,则返回x(y)
    for (int i=lg[depth[x]];i>=0;i--)
        if (fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];//往上跳
    return fa[x][0];//返回x(y)的父亲
}
int main()
{
    n=read();m=read();s=read();//输入
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();add(x,y);//邻接表
    }
    dfs(s,0);//计算每个点的深度以及它们的2^j(0..n)级祖先
    CSH();//常数优化
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        printf("%d\n",LCA(x,y));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39798042/article/details/81663362
今日推荐