树链剖分求lca

197 阅读2分钟

树链剖分

原理

树链剖分是根据轻重儿子,将一棵树剖成多条链,然后就可以用数据结构来维护这些链了,听着似乎还是有点像暴力,不过因为一条链有多个结点,所以可以优化时间复杂度。

树上LCA2

给你一棵 n 个节点的以 11 号节点为根的树,节点的编号为 1 到 n。现在有 m 组询问,对于每组询问 u,v,请求出 u 号节点和 v 号节点的最近公共祖先。

输入格式

第一行一个整数 n 表示节点数。

接下来 n−1 行,每行两个整数 x,y表示 x 号节点和 y 号节点之间有一条边。

输入保证是一棵树。

接下来一行一个整数 m 表示询问数。

接下来 m 行,每行两个整数 u,v表示一组询问。

输出格式

输出共 m 行,对于每组询问,输出一行一个整数表示对应的两个点的最近公共祖先的编号。

样例输入

4
1 2
1 3
3 4
2
1 2
2 4

样例输出

1
1

数据规模

对于所有数据,保证 1≤n,m≤100000,1≤u,v≤n。

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n,m,tot;
int l[N],dep[N],fa[N],top[N],sz[N],hs[N],id[N];
vector<int>e[N];
void dfs1(int u,int v)
{
	sz[u] = 1;
	hs[u] = -1;
	dep[u] = dep[v] + 1;
	fa[u] = v;
	for(auto c:e[u])
	{
		if(c == v)
		continue;
		dfs1(c,u);
		sz[u] += sz[c];
		if(sz[c] > sz[hs[u]])
		hs[u] = c;
	}
}
void dfs2(int u,int v)
{
	top[u] = v;
	l[u] = ++tot;
	id[tot] = u;
	if(hs[u] != -1)
	dfs2(hs[u],v);
	for(auto c:e[u])
	{
		if(hs[u]!=c&&fa[u]!=c)
		dfs2(c,c);
	}
}

int lca(int u,int v)
{
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]])
		{
			v = fa[top[v]];
		}
		else
		u = fa[top[u]];
	}
	
	if(dep[u] < dep[v])
	return u;
	else
	return v;
}
int main()
{
	cin >> n;
	for(int i = 1;i < n;i ++)
	{
		int x,y;
		cin >> x >> y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	dfs1(1,0);
	dfs2(1,1);
	cin >> m;
	for(int i = 1;i <= m;i ++)
	{
		int x,y;
		cin >> x >> y;
		cout << lca(x,y) << '\n';
	}
}