ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LCA(最近公共祖先)

2022-09-03 19:34:32  阅读:69  来源: 互联网

标签:rt dep 祖先 void LCA dfs int edge 公共


lca,即最近公共祖先。最近公共祖先,顾名思义,就是树上两个点最近的祖先。

我们大体上有三个算法来搞。

第一个:\(O(nlogn)\)预处理,\(O(1)\)查询。

大体上是借用了rmq问题的思路(就是区间最大/小值)来处理。

将树上问题转化为区间问题。

void dfs(int rt,int d){
	v[rt]=true;num[++t]=rt;first[rt]=t;dep[t]=d;//处理深度和dfs序 
	for(int i=head[rt];i;i=edge[i].next){
		if(!v[edge[i].v]){
			dis[edge[i].v]=dis[rt]+edge[i].w;
			dfs(edge[i].v,d+1);
			num[++t]=rt;dep[t]=d;//dfs序不解释 处理出每个节点控制的区间 
		}
	}
}
void st(){
	for(int i=1;i<=t;i++)st[i][0]=i;
	for(int j=1;(1<<j)<=t;j++){
		for(int i=1;i+(1<<j)-1<=t;i++){
			int a=st[i][j-1],b=st[i+(1<<(j-1))][j-1];
			if(dep[a]<dep[b])st[i][j]=a;
			else st[i][j]=b;
		}
	}
}//就是普通st表的预处理
int rmq(int l,int r){
	int k=0;
	while((1<<(k+1))<=r-l+1)k++;
	int a=st[l][k],b=st[r-(1<<k)+1][k];
	if(dep[a]<dep[b])return a;
	return b;
}
int lca(int u,int v){
	int x=first[u],y=first[v];
	if(x>y)swap(x,y);
	return num[rmq(x,y)];
}

然后第二种:tarjan的离线算法。大体上借用了并查集来搞。

向上回溯法,没搜标记0,搜完标记2,开始搜但是没搜完标记1。

当一个节点搜完时把它和父亲合并,即每个节点都能指向它搜完的最上面的祖先。

void tarjan(int x){
	v[x]=1;
	for(int i=head[x];i;i=edge[i].next){
		if(!v[edge[i].v]){
			tarjan(edge[i].v);
			merge(x,edge[i].v);//搜完儿子 合并 
		}
	}
	for(int i=0;i<ques[x].size();i++){
		int y=ques[x][i],id=quesid[x][i];//离线处理关于x的每个问题 
		if(v[y]==2)lca[x][y]=find(y);//并查集 
	}
	v[x]=2;
}

最后是第三种(也是最常用的之一):\(O(nlogn)\)预处理,\(O(logn)\)查询的倍增算法。而且超级短。

思路很简单,我们找到树上两个点的时候倍增地向上跳祖先,一直跳到lca。

int f[100010][21];//我曾经不知道多少次把这个写成20然后re
void dfs(int rt,int d){
    v[rt]=true;dep[rt]=d;
    for(int i=head[rt];i;i=edge[i].next){
        if(!v[edge[i].v]){
            f[edge[i].v][0]=rt;
            for(int j=1;j<=20;j++){
                f[edge[i].v][j]=f[f[edge[i].v][j-1]][j-1];
            }
            dfs(edge[i].v,d+1); 
        }
    }
}
int lca(int x,int y){
    if(dep[x]>dep[y])swap(x,y);
    for(int i=20;i>=0;i--){
        if(dep[f[y][i]]>=dep[x]){
            y=f[y][i];
        }
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            x=f[x][i];y=f[y][i];
        }
    }
    return f[x][0];
} 

标签:rt,dep,祖先,void,LCA,dfs,int,edge,公共
来源: https://www.cnblogs.com/gtm1514/p/16653353.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有