最近公共祖先(LCA)

最近公共祖先(LCA)

一道板题

倍增求LCA


维护数组 \(f[ i ][ j ]\) 表示\(i\) 号节点的第\(2^j\) 级祖先,没有则为\(0\)

转移方程:

\[f[i][j] =f[f[i][j-1]][j-1] (j>0) \]

\[f[i][j]=fa[i] (j=0) \]


  • 相同深度求LCA:

将k从大到小循环,如果\(x\)\(y\)\(2^k\) 级祖先相同则不做改变,不同则将 \(x\)\(y\) 更新为各自的\(2^k\) 级祖先。

\(k=0\) 时,LCA为此时 \(x\)\(y\) 的父亲

  • 不同深度求LCA:

\(dep[a]>dep[b]\) ,将 \(a\) 向上跳,直到 \(a\) 的深度与 \(b\) 的深度相同为止,于是变转换为 相同深度求 LCA

时间复杂度:
预处理: \(O(nlogn)\) ,单词询问: \(O(logn)\)

空间复杂度: \(nlogn\)


代码示例:

预处理:

void bfs(int root){
	memset(d,0x3f,sizeof d);
	d[root]=1,d[0]=0;
	queue<int> q;
	q.push(root);
	
	while(q.size()){
		int t=q.front();
		q.pop();
		
		for(int i=h[t];i;i=nxt[i]){
			int x=en[i];
			if(d[x]>d[t]+1){
					d[x]=d[t]+1;
				   q.push(x);
				   f[x][0]=t;
				   for(int j=1;j<=15;j++)
						f[x][j]=f[f[x][j-1]][j-1];
			}
		}
	}
}

主体:

int lca(int a,int b){
	if(d[a]<d[b])	swap(a,b);
	
	for(int i=15;i>=0;i--)
		if(d[f[a][i]]>=d[b])	a=f[a][i];
	
	if(a==b)	return a;
	
	for(int i=15;i>=0;i--)
		if(f[a][i]!=f[b][i])
			a=f[a][i],b=f[b][i];
	    
	return f[a][0];
}
    

tarjan求LCA


利用递归的回溯做标记,遍历一次后标记为\(1\) ,回溯后标记为\(2\)

把询问转换成离线做法,先把所有的询问存下来,最后统一输出

优化用到了并查集的思想,回溯后将此点与其父亲节点合并,答案返回集合的父亲节点


代码示例:

存询问:

void add_qu(int a,int b,int i){
	qu[a].push_back(b),id[a].push_back(i);
	qu[b].push_back(a),id[b].push_back(i);
}

主体:

void targan(int x){
	v[x]=1;
	for(int i=h[x];i;i=nxt[i]){
		int y=en[i];
		if(v[y])	continue;
		targan(y);
		f[y]=x;
	}
	
	for(int i=0;i<qu[x].size();i++){
		int y=qu[x][i],idd=id[x][i];
		if(v[y]!=2)	continue;
		ans[idd]=get(y);
	}
	    
	v[x]=2;
}

时间复杂度: \(O(n+m)\)

还是贴个完整代码吧:

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5,M=N*2;
int h[N],nxt[M],en[M],m;
vector<int> qu[N],id[N];
int root;
int v[N],ans[N],f[N];

void add(int a,int b){
	nxt[++m]=h[a],en[m]=b,h[a]=m;
}
void add_qu(int a,int b,int i){
	qu[a].push_back(b),id[a].push_back(i);
	qu[b].push_back(a),id[b].push_back(i);
}

int get(int x){
	if(f[x]==x)	return x;
	return f[x]=get(f[x]);
}

void targan(int x){
	v[x]=1;
	for(int i=h[x];i;i=nxt[i]){
		int y=en[i];
		if(v[y])	continue;
		targan(y);
		f[y]=x;
	}
	
	for(int i=0;i<qu[x].size();i++){
		int y=qu[x][i],idd=id[x][i];
		if(v[y]!=2)	continue;
		ans[idd]=get(y);
	}
	
	v[x]=2;
}

int main(){
	
	int n,k;
	scanf("%d%d%d",&n,&k,&root);
	
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b),add(b,a);
		f[a]=a,f[b]=b;
	}
	
	for(int i=1;i<=k;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		if(a==b)	ans[i]=a;
		else	add_qu(a,b,i);
	}
	
	f[root]=root;
	targan(root);
	
	for(int i=1;i<=k;i++)	printf("%d\n",ans[i]);
	
	return 0;
}
posted @ 2022-02-15 14:48  _yolanda  阅读(29)  评论(0编辑  收藏  举报