其他分享
首页 > 其他分享> > LCA 返回最近公共祖先:预处理

LCA 返回最近公共祖先:预处理

作者:互联网

三种祖先关系 a是b祖先 b是a祖先 a和b不是祖先关系

必备:知道根节点 必须存下来

有可能跳过根节点
int depth[N],f[][N];//N为(log节点数)+1
int q[N];

从根节点开始预处理;
需要设置0号点为哨兵
询问 p=lca(a,b)

向上标记法on:从一个点向根节点遍历标记公共祖先 ,然后另一个点也向上走 走到第一个标记过的位置 就是祖先

倍增法ologn 预处理nlogn 查询logn:f[i][j]从i开始 向上走2^j次方 走到的节点

f[i][0]i点向上走一步走到的节点 f[i][1]向上走两步走到的节点 f[i][2]向上走四步发现不存在值为空集
当j>0 f[i][j]=f[i][j-1]基础上2^j-1步
depth表示深度从上往下看的 根节点深度是1 i表示i节点的深度
于是lca
哨兵:如果f[i][j]跳过根节点那么设为0 0节点的深度为0
第一步让两个点跳到同一层: t=相差depth(x)-depth(y)层 根据t的二进制跳 如果跳了不在y的上面那就跳:depth(f(x,k))>depth(y) 所以挑了之后就还能跳
第二步让两个点同时往上跳,直到跳到最近公共祖先的下一层:此时最近公共祖先为f[i][0]

祖孙查询https://www.acwing.com/activity/content/problem/content/1537/

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 40010,M=2*N;

int n,m;
int h[N], e[M], ne[M], idx;
int depth[N],fa[N][16];
int q[N];
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int root){//从上到下
    memset(depth,0x3f , sizeof depth);
    depth[0]=0,depth[root]=1;//0号哨兵点深度为0,根节点深度为1
    int hh=0,tt=0;//bfs队列
    q[0]=root;
    while(hh<=tt){
        int t=q[hh++];
        for (int i = h[t]; ~i ; i =ne[i] ){
            int j=e[i];//对于当前点的子节点
            if(depth[j]>depth[t]+1){//既然连了边,又比+1大说明是前面设置0正无穷的缘故
                depth[j]=depth[t]+1;
                q[++tt]=j;
                fa[j][0]=t;
                for (int k = 1; k <= 15; k ++ ){//因为fa初始化是0所以只要走不上求自动为0
                    fa[j][k]=fa[fa[j][k-1]][k-1];//j点在上面第k-1的祖先往上走k-1步
                }
            }
        }
    }
}
int lca(int a,int b){//两个fork从大到小实现 
    if(depth[a]<depth[b]) swap(a,b);//要求a在b的下面 不满足就换
    for (int k = 15; k >=0; k -- ){//从大到小开始走
        if(depth[fa[a][k]]>=depth[b])
            a=fa[a][k];
    }
    if(a==b) return a;
    for (int k = 15; k >=0; k -- ){
        if(fa[a][k]!=fa[b][k]){//说明不在公共祖先上 一起跳
            a=fa[a][k];
            b=fa[b][k];
        }
    }
    return fa[a][0];//向上走一步
}
int main()
{
    cin >> n;
    int root=0;
    memset(h, -1, sizeof h);
    for (int i = 0; i < n; i ++ ){
        int a,b;
        cin >> a>>b;
        if(b==-1) root=a;
        else add(a,b),add(b,a);
    }
    
    bfs(root);//预处理
    cin >> m;   
    while (m -- ){
        int a,b;cin>>a>>b;
        int p=lca(a,b);
        if(p==a) cout << 1<<endl;
        else if(p==b) cout << 2<<endl;
        else cout << 0<<endl;
    }
    return 0;
}

标签:fa,祖先,预处理,int,depth,LCA,root,节点
来源: https://www.cnblogs.com/liang302/p/16517340.html