树链剖分求LCA——杨子曰算法

树链剖分求LCA——杨子曰算法

显然这是树链剖分和LCA的结合体
然后假装你已经会了这两个东西,或者看了:
树链剖分——杨子曰算法
谈谈最近公共祖先(LCA)——杨子曰算法
这两个东西,如果你对上面那两个玩意有印象的话,黑喂狗:


树链剖分求LCA思路非常的简单,也非常好理解:
1.按重儿子剖分这棵树
这时对于同一条链上的两个点直接输出高的那个就行了,那如果两个点不在同一条链上呢?——我们又要开始跳了
2.只要两个点不在同一条链上,就重复3,否则进行4:

3.将x,y中所在链的链顶较低的点,跳到这条链的链顶的父亲

4.现在x,y在一条链上了,输出较高的点,完事!

很多大佬一定都已经明白了,我们再来模拟一下,我去我树链剖分那篇文章里偷个图:
树链剖分求LCA——杨子曰算法_第1张图片
假设我们现在要求x和y的LCA:
发现它们不在同一条链上,于是我们比较绿色链链顶和蓝色链链顶的深度,于是我们发现绿色链的链顶较低把x调到绿色链链顶的父亲1
又发现x,y还是不在同一条链上,我们又来比较红色链链顶和蓝色链链顶,我们把y调到蓝色链链顶的父亲2
现在x和y在同一条链上了!输出较高的y处在的位置结点2——就是它们的LCA

树链剖分法求LCA的优点:
1.代码不是特别的长
2.非常好理解
3.空间比倍增小

OK,完事


c++模板(洛谷P3379):

#include
using namespace std;

const int maxn=500005;

struct Edge{
	int next,to;
}edge[maxn*2];

int nedge=0;
int head[maxn],f[maxn],d[maxn],son[maxn],sz[maxn],tp[maxn];

void addedge(int a,int b){
	edge[nedge].to=b;
	edge[nedge].next=head[a];
	head[a]=nedge++;
}

void dfs1(int x,int fa,int dep){
	f[x]=fa;
	d[x]=dep;
	sz[x]=1;
	for (int i=head[x];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if (v==fa) continue;
		dfs1(v,x,dep+1);
		sz[x]+=sz[v];
		if (!son[x] || sz[son[x]]<sz[v]) son[x]=v;
	}
}

void dfs2(int x,int fa,int top){
	tp[x]=top;
	if (son[x]) dfs2(son[x],x,top);
	for (int i=head[x];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if (v==fa || v==son[x]) continue;
		dfs2(v,x,v);
	}
}

int lca(int x,int y){
	while(tp[x]!=tp[y]){
		int f1=tp[x],f2=tp[y];
		if (d[f1]>d[f2]) x=f[f1];
		else y=f[f2];
	}
	if (d[x]>d[y]) return y;
	else return x;
}

int main(){
	memset(head,-1,sizeof(head));
	int n,m,r;
	scanf("%d%d%d",&n,&m,&r);
	for (int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		addedge(a,b);
		addedge(b,a);
	}
	dfs1(r,0,1);
	dfs2(r,0,r);
	while(m--){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	}
	return 0;
}

于XJ机房607

你可能感兴趣的:(变态的算法,算法与数据结构)