题目(不是板子!)-最近公共祖先(LCA)


第4题     最近公共祖先 查看测评数据信息

小 Soup 正在翻看他们家的族谱,他们家的族谱构成了一棵树。小 Soup 发现,由于年代久远,他们家族中的一些分支已经绝迹,他对此十分好奇。

小 Soup 给你他们家的族谱树,想要问你在这棵树中所有第 k 层的孩子(树中深度为 k 的点,根节点的深度为 1 ,根节点编号为 1 )的最近公共祖先是谁。

输入格式

 

第一行两个整数 n,m。  

第二行 n 个整数,其中第 i 个整数为 f[i],表示 i 的父亲为 f[i],请注意,1 的 f[i] 固定为 0。  

接下来 m 行,每行一个整数 k,代表小 Soup 的询问。

%20 n[1,10],m[1,10]

%20 n[1,100],m[1,100]

%20 n[1,1000],m[1,1000]

%20 n[1,100000],m[1,100000]

%20 n[1,5000000],m[1,5000000]

 

 

输出格式

 

对于每个小 Soup 的询问,输出一个整数,即所有深度为 k 的点的最近公共祖先。

 

输入/输出例子1

输入:

8 3

0 1 1 2 2 3 4 5

2

1

4

 

输出:

1

1

2

 

样例解释

 

 

两个思路

1.记录每层中最左,最右的节点,找最左最右最近公共祖先  ( 缺证)
2.自底向上bfs,记录所有答案

 

这里只讨论第一种。

关于最左最右很好整,最左是dfs中最先到的那个点,最右是dfs中最后到的那个点,分别记录即可。

#include <bits/stdc++.h>
using namespace std;
const int N=100005;

int n, m, x, k, vis[N], dep[N], first[N], last[N];
int f[N][25];
vector<int> a[N];
void dfs(int u, int fa)
{
	if (vis[u]) return ;
	vis[u]=1;
	
	dep[u]=dep[fa]+1;
	
	f[u][0]=fa;
	for (int i=1; (1<<i)<=dep[u]; i++)
		f[u][i]=f[f[u][i-1]][i-1];
	
	if (!first[dep[u]]) first[dep[u]]=u;
	last[dep[u]]=u;
	
	for (int i=0; i<a[u].size(); i++)
	{
		int v=a[u][i];
		if (v!=fa) dfs(v, u);
	}
}
int lca(int x, int y)
{
	if (dep[x]<dep[y]) swap(x, y);
	
	for (int i=24; i>=0; i--)
		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
		
	if (x==y) return x;
	
	for (int i=24; i>=0; i--)
		if (f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
	
	return f[x][0];
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i=1; i<=n; i++) 
	{
		scanf("%d", &x);
		a[x].push_back(i);
		a[i].push_back(x);
	}
	
	dep[1]=1;
	dfs(1, 0);
	
	while (m--)
	{
		scanf("%d", &k);
		printf("%d\n", lca(first[k], last[k]));
	}
	return 0;
}

  

 

 

posted @ 2024-07-14 21:22  cn是大帅哥886  阅读(8)  评论(0编辑  收藏  举报