ST表求LCA

https://www.luogu.org/problemnew/show/SP14932

思路:先遍历一遍得到欧拉序,然后根据询问找到对应节点在欧拉序第一次出现的位置.left, right,然后欧拉序中在left到right之间的深度最小的节点就是LCA。

如上图:

从4遍历得到的欧拉序是 4 2 4 1 3 1 5 1 4

得到的对应节点深度      1 2 1 2  3 2 3 2 1

比如查询3 和 2 的最近公共祖先,3 和 2 在欧拉序中第一次出现的位置 是 2 和 5 ,在欧拉序中他们之间的点(包括自己)有

2 4 1 3 ,深度最小是 1 ,即 节点4。所以3 和2 的LCA是4

 

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;

const int  maxn   = 5e5 + 5;
const int maxbit = 20;
vector<int>G[maxn];
int order[maxn * 4];//记录欧拉序
int depth[maxn * 4];//记录欧拉序序列每个点的深度
int n,m,s;//节点个数,询问次数,根节点
int lg[maxn * 4];//以2为底向下取整
int ST[maxn * 4][maxbit];//记录长度是2的p次方的欧拉序列的深度最小的下标
int cnt = 0;//记录在欧拉序是第几个
int first_place[maxn * 4];//记录i节点第一次出现的位置

void dfs(int nownode, int fdepth)//确定欧拉序每个节点的深度和第一次出现的位置
{
	//nownode是当前节点,fdepth是其父节点深度
	cnt++;
	first_place[nownode] = cnt;//欧拉序第cnt个节点的深度
	order[cnt] = nownode;//欧拉序第cnt个节点是nownode
	depth[cnt] = fdepth +1;//欧拉序第cnt个节点(即当前节点)深度为父节点深度+1
	for(int i = 0; i < G[nownode].size(); i++)
	{
		int sonnode = G[nownode][i];
		if(first_place[sonnode] == 0)//没有遍历过的子节点
		{
			dfs(sonnode, fdepth + 1);
			cnt++;//欧拉序的回溯也要记录
			order[cnt] = nownode;//同上
			depth[cnt] = fdepth + 1;//同上

		}
	}


}

void ST_init()//建ST表
{
	//初始每个节点都是深度最小的节点
	for(int i = 1; i <= cnt; i++)
		ST[i][0] = i;

	int a, b;
	for(int j = 1; j <= lg[cnt]; j++)
	{
		for(int i = 1; i + (1<<j) - 1 <= cnt; i++)
		{
			a = ST[i][j - 1];
			b = ST[i + ( 1<<(j - 1) ) ][ j-1];
			if(depth[a] < depth[b])
				ST[i][j] = a;//记录深度最小节点
			else
				ST[i][j] = b;
		}

	}
}


int main()
{

	ios::sync_with_stdio(false);
	lg[0] = -1;//初始对数
	for(int i = 1;  i < maxn * 4; i++)
		lg[i] = lg[i>>1] +1;

	scanf("%d%d%d", &n, &m, &s);
	int x, y;

	for(int i = 0; i < n-1; i++)//构造图
	{
		scanf("%d%d", &x, &y);
		G[x].push_back(y);
		G[y].push_back(x);

	}
	dfs(s, 0);
	ST_init();
	while(m--)
	{
		scanf("%d%d", &x, &y);
		x = first_place[x];
		y = first_place[y];
		if(x > y)
			swap(x, y);
		int k = (y - x) + 1;
		k = lg[k];
		int a, b;
		a = ST[x][k];
		b = ST[y - (1<<k) + 1][k];
		if(depth[a] < depth[b])
		{
			printf("%d\n", order[a]);
		}
		else
			printf("%d\n", order[b]);
	}

	
	//system("pause");

	
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值