……

学习笔记:树剖求 LCA

我之前介绍过 tarjan 求 LCA 的方法:链接-->戳我!
然而离线算法又长又臭,果真烦人,于是我们尝试用树剖解决问题。
我们联想暴力求 LCA 的方法,发现在跳父亲的时候有一些点可以一下子跳过去。
而树剖便是把链们作为一个整体,由于 \(top\) 数组的存在,直接跳到 \(f_{top}\) 即可。
复杂度容易证明是 \(\mathcal O((n+m)\log n)\),常数比倍增小,不算很难写(至少我一遍 \(A\)

\(Code\):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;

#define read(x) scanf("%d",&x)
#define MAXN 500005

int top[MAXN],dep[MAXN],son[MAXN],f[MAXN],tot[MAXN];
struct node
{
	int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int n,m,root,l,r;

void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}

int dfsa(int cur,int deep)
{
	dep[cur]=deep;
	tot[cur]=1;
	son[cur]=0;
	int maxn=0;
	for(int i=head[cur];i;i=e[i].nxt)
	{
		int j=e[i].to;
		if(j==f[cur]) continue;
		f[j]=cur;
		int now=dfsa(j,deep+1);
		if(now>maxn) maxn=now,son[cur]=j;
		tot[cur]+=now;
	}
	return tot[cur];
}

void dfsb(int cur,int topf)
{
	top[cur]=topf;
	if(son[cur]) dfsb(son[cur],topf);
	for(int i=head[cur];i;i=e[i].nxt)
	{
		int j=e[i].to;
		if(top[j]) continue;
		dfsb(j,j);
	}
	return;
}

int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=f[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int main()
{
	read(n),read(m),read(root);
	for(int i=1;i<n;i++)
	{
		read(l),read(r);
		add(l,r),add(r,l);
	}
	f[root]=root;
	dfsa(root,1);
	dfsb(root,root);
	for(int i=1;i<=m;i++)
	{
		read(l),read(r);
		printf("%d\n",lca(l,r));
	}	
	return 0; 
}
posted @ 2020-04-29 07:45  童话镇里的星河  阅读(142)  评论(0编辑  收藏  举报