首页 > 其他分享 >LCA

LCA

时间:2023-08-02 15:55:05浏览次数:35  
标签:dep int fa LCA include 节点

目录

LCA最近公共祖先

最近公共祖先简称 LCA(Lowest Common Ancestor)。两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个。

暴力求解太慢,这里先摘记一种做法-倍增法
对于每一个节点,我们先通过dfs预先处理出当前节点向根移动2的幂次方的节点编号(有种st表的感觉)。转移方程:\(fa[u][i]=fa[fa[u][i-1]][i-1],i\ge 1\)
关键数组:深度数组dep和父节点?数组fa

void dfs(int x,int f){
	dep[x]=dep[f]+1;
	fa[x][0]=f;
	for(int i=1;i<=N;++i){
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=p[i].next){
		if(p[i].to!=f) dfs(p[i].to,x);
	}
	return;
}

对于询问,首先让u与v处于同一深度,此处排除当前u和v即为最近公共祖先的情况。之后让u和v一起向根节点游,\(fa[u][i]!=fa[v][i]\),则继续向上游。直到最后答案为\(fa[v][0]\)

int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);//使u深度大
	for(int i=N;i>=0;--i){
		if(dep[fa[u][i]]>=dep[v]){//为了处于同一深度
			u=fa[u][i];
		}
	}
	if(u==v) return v;//当前节点即为最近公共祖先
	for(int i=N;i>=0;--i){
		if(fa[u][i]!=fa[v][i]){//一起向上
			u=fa[u][i];
			v=fa[v][i];
		}
	}
	return fa[v][0];
}

例题

模板题

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
最近公共祖先模板题
利用深搜构造lca数组
*/
const int maxm=5e5+5,inf=0x3f3f3f3f,mod=998244353,N=20;
int n,m,s,cnt=1,head[maxm],dep[maxm],fa[maxm][N+1];

struct node{
	int to,next;
}p[maxm<<1];

void add_edge(int a,int b){
	p[cnt].to=b;
	p[cnt].next=head[a];
	head[a]=cnt++;
	return ;
}

void dfs(int x,int f){
	dep[x]=dep[f]+1;
	fa[x][0]=f;
	for(int i=1;i<=N;++i){
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=p[i].next){
		if(p[i].to!=f) dfs(p[i].to,x);
	}
	return;
}

int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=N;i>=0;--i){
		if(dep[fa[u][i]]>=dep[v]){
			u=fa[u][i];
		}
	}
	if(u==v) return v;
	for(int i=N;i>=0;--i){
		if(fa[u][i]!=fa[v][i]){
			u=fa[u][i];
			v=fa[v][i];
		}
	}
	return fa[v][0];
}

void solve(){
	cin>>n>>m>>s;
	int a,b;
	for(int i=1;i<n;++i){
		cin>>a>>b;
		add_edge(a,b);
		add_edge(b,a);
	}
	dfs(s,0);
	while(m--){
		cin>>a>>b;
		cout<<lca(a,b)<<'\n';
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

根据题意抽象为树,再利用倍增优化。具体代码与lca模板题巨像
代码:
链式前向星 qiansui_code
邻接表 qiansui_code


综合题

相关资料

整体介绍
https://oi-wiki.org/graph/lca/

模板及视频讲解:
倍增法 https://www.cnblogs.com/dx123/p/16320461.html

标签:dep,int,fa,LCA,include,节点
From: https://www.cnblogs.com/Qiansui/p/17600900.html

相关文章

  • PascalCase & camelCase & kebabCase介绍
    原文链接:https://www.cnblogs.com/qdkfyym/p/13528076.html一、PascalCase  帕斯卡拼写法(也叫大骆驼拼写法)  PascalCase 帕斯卡拼写法是一种计算机编程中的变量命名方法。它主要的特点是将描述变量作用所有单词的首字母大写,然后直接连接起来,单词之间没有连接符。比......
  • 2167 - 树的公共祖先(LCA)
    题目描述给定一棵树和两个不同的结点,求出他们最近的公共祖先父结点。已知该树有n个结点,标号1..n。输入第1行输入一个整数nn,代表结点数量(n≤100)第2行输入两个整数x,yx,y,表示需要计算的结点;以下n−1行,每行两个整数a和b,表示a的父结点是b。输出输......
  • 【模板】最近公共祖先(LCA)
    postedon2021-08-0414:22:40|under学术|sourceLCA,LeastCommonAncestors,最近公共祖先。倍增。首先预处理出数组\(d_i\)和\(f_{i,j}\)。\(d_i\)表示第\(i\)个节点的深度。转移方程:\(d_{i}=d_{\text{fa}}+1\)\(f_{i,j}\)表示第\(i\)个节点的第\(2^j\)级......
  • GlobalCache 工具类
    packagecom.neo.config;importorg.springframework.stereotype.Component;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurren......
  • 最近公共祖先(LCA)
    向上标记法时间复杂度:$O(n)$待查询点为a和b,首先从a点向根节点进行搜索,将路径上的点进行标记,再从b点向根节点进行搜索,同时检测路径上的点是否被标记过,第一次检测到的点即为a,b两点的最近公共祖先倍增时间复杂度预处理:$O(nlog_n)$查询:$O(log_n)$相关概念及性质节点祖先:从......
  • 冷门科技 —— DFS 序求 LCA
    Updateon2023.7.17:该技巧目前已知的最早来源:skip2004。Updateon2023.7.17:比较时,取时间戳较小的结点也是正确的,不用记录深度。DFS序求LCA无论是从时间常数,空间常数还是好写程度方面均吊打欧拉序。定义DFS序表示对一棵树进行深度优先搜索得到的结点序列,而时间戳DFN......
  • LCA 离线tarjan算法
    tarjan算法是离线算法,它必须先将所有的要查询的点对存起来,然后在搜的时候输出结果。tarjan算法很经典,因为算法的思想很巧妙,利用了并查集思想,在dfs下,将查询一步一步的搜出来。伪代码如下:可以看到,对于我们已经保存好的查询,假设为(u,v),u为此时已经搜完的子树的根节点,v的位置就只有两种......
  • 最近公共祖先(LCA)
    最近公共祖先(LCA)主要思路:一个节点的\(2^n\)的祖先是那个节点\(2^{n-1}\)的祖先的\(2^{n-1}\)的祖先也就是说\(f[x][n]=f[f[x][n-1]][n-1]\)还要计算\(log_2\)的值,记录深度#include<bits/stdc++.h>usingnamespacestd;constintMAXN=5e5+10;intfa[MAXN][30],lg[M......
  • [算法学习笔记] Tarjan LCA
    在讲解之前,我们先来看一道模板题:LuoguP3379最近公共祖先(LCA)WhatisLCALCA,即最近公共祖先。什么意思呢,我们举个例子:将就着看吧qwq这棵树中,0为根节点。若规定\(LCA(x,y)\)为\(x,y\)的最近公共祖先,则\(LCA(5,6)=2;LCA(4,3)=1;LCA(5,3)=0\)。还有很多,这里不一一列举了。最......
  • 第七章 LCA计算与分析
    7.1生命周期清单(LCI)的算法 7.2生命周期影响类型指标(LCIA)的算法 ......