两种特别有用的求LCA的方法

第一种:树链剖分@TOC
一:知识储备

  1. 重节点:以i为根的节点中结点数最多的结点
  2. 轻结点:其他结点
  3. 重链:由重节点连成的链

二:实现必需品:

  1. dep存深度
  2. son存重节点是谁
  3. siz存以i为根的子树大小
  4. fa存父亲是谁
  5. top存“头”
    ~重节点的头是他所在链的第一个出现的点
    ~轻结点的头是他自己

三:代码:

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn=100; 
//siz是它对应的结点的子树个数,算上他自己
//son存储这个点的重儿子 
struct Edge{
	int to;//下一个点 
	int val;//边权 
};
//这里使用邻接表存图,和链式前向星一样 
vector<Edge>g[maxn];
int fa[maxn],siz[maxn],son[maxn],dep[maxn];
int top[maxn];
//求出每个点的重儿子 
void dfs1(int u){
	siz[u]=1;
	son[u]=0;
	for(int i=0;i<g[u].size();i++){//循环边 
		Edge &v=g[u][i];
		if(fa[u]==v.to)continue;
		dep[v.to]=dep[u]+1;
		fa[v.to]=u;
		dfs1(v.to);
		if(siz[son[u]]<siz[v.to])son[u]=v.to;
		siz[u]+=siz[v.to];
	}
}
//若他在一条重链上,他的top就是第一个出现的点
//反之,他的top就是他自己 
//求每个点的top 
void dfs2(int u,int tp){
	top[u]=tp;
	if(son[u])dfs2(son[u],tp);//走重链 
	for(int i=0;i<g[u].size();i++){
		Edge &v=g[u][i];
		if(v.to==son[u]||v.to==fa[u])continue;
		//若是一个重儿子,因为已经走过就不走了
		//若是父亲当然也不走 
		dfs2(v.to,v.to);
	}
} 
int lca(int u,int v){
	int fu=top[u];
	int fv=top[v];
	while(fu!=fv){
		if(dep[fu]<dep[fv]){
			swap(fu,fv);
			swap(u,v);
		}
		//跳的是深度较深的 
		u=fa[fu];
		fu=top[u];
	}
	if(dep[u]<dep[v])swap(u,v);
	//返回的是深度小的那个
	//因为他们这时的top相同,说明在一条重链上,深度较浅的那个是lca 
	return v;
}
int main() {
	int n;
	scanf("%d",&n);
	for(int i=0;i<n-1;i++){
		int u,v,val;
		scanf("%d%d%d",&u,&v,&val);
		g[u].push_back((Edge){v,val});
		g[v].push_back((Edge){u,val});
	} 
	dep[1]=1;
	dfs1(1);
	cout<<"a";
	dfs2(1,1);
	cout<<"b";
	while(1){
		int u,v;
		cin>>u>>v;
		cout<<lca(u,v);
	}
	return 0;
}

第二种:倍增求LCA
一:实现必需品

  1. dp存储这个点向上跳2的k次方步之后的点,这个点的祖先
  2. dep存储点的深度

二:实现步骤

  1. 首先把深度预处理出来
  2. 把两个点的深度跳到相同,再一起往上跳,直到跳到相同深度
  3. 求LCA

三:代码

#include <iostream>
int dp[maxn][20];
int dep[maxn];
//首先把深度跳到相同,再一起往上跳 ,直到跳相同的深度 
void dfs(int u,int fa,int h){
   dep[u]=h;
   int t=0;
   while(dp[u][t]){
   	dp[u][t+1]=dp[dp[u][t]][t];
   	t++;
   }
   for(int i=0;i<g[u].size();i++){
   	int now=g[u][i];
   	if(now==fa)continue;
   	dp[now][0]=u;
   	dfs(now,u,h+1);
   }
}
int lca(int u,int v){
   if(dep[u]<dep[v])swap(u,v);
   //先跳到相同节点 
   for(int i=19;i>=0;i--){
   	//根节点的深度要为1,不然他们可能跳过头 
   	if(dep[dp[u][i]]>=dep[v]){
   		u=dp[u][i];
   	}
   }
   if(u==v)return u;
   for(int i=19;i>=0;i--){
   	if(dp[u][i]^dp[v][i])u=dp[u][i],v=dp[v][i];
   	//两个相同的数异或起来等于0 
   	//看他们的公共祖先 
   }
   return dp[u][0];
}
int main() {
   scanf("%d%d",&n,&m);
   for(int i=0;i<n-1;i++){
   	int a,b;
   	scanf("%d%d",&a,&b);
   	g[a].push_back(b);
   	g[b].push_back(a);
   }
   return 0;
}

猜你喜欢

转载自blog.csdn.net/summer20020929/article/details/83582301