[每日一题]: E. Tree Queries -- 最近公共祖先

题目:

题目大意:

给一棵树,然后从树上拿出来几个点,问这几个点是否在同一条链上或者某些点和这条链的距离是否为1,满足
这样的条件 即 Yes,反之则 No.

考察点:

LCA、最近公共祖先

侃侃:

同一条链上的点有啥特征呢?
在同一条链上,最深的点与较浅的点的最近公共祖先一定是 较浅的点。
所以,我们遍历所有的 K 个点,然后找出最深的一个点。
求所有点和这个最深的点的 LCA,如果得到的 LCA 是较浅的点本身说
明一定在同一条链上,距离为 1 的点我们可以考虑父节点,如果说较浅
的点的父节点和 最深的点的 LCA 是其本身,那么也是符合条件的。
反之则不符合。

Code:

#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 2e5 + 10;

int head[maxn << 1],Next[maxn << 1],edge[maxn << 1],ver[maxn << 1];

int dp[maxn][25],deep[maxn],value[maxn];

int n,m,k,tot;

int u,v,t;

void add(int u,int v) {
	ver[++ tot] = v,Next[tot] = head[u];
	head[u] = tot;
	return ;
}

// 预处理 
void BFS() {
	queue<int>Q;
	Q.push(1),deep[1] = 1;
	while(Q.size()) {
		int x = Q.front();
		Q.pop();
		for(int i = head[x]; i; i = Next[i]) {
			int y = ver[i];
			if(deep[y]) continue;
			deep[y] = deep[x] + 1;
			dp[y][0] = x;
			for(int j = 1; j <= t; j ++) {
				dp[y][j] = dp[dp[y][j - 1]][j - 1];
			}
			Q.push(y);
		} 
	}
	return ; 
}

int LCA(int x,int y) {
	// 只调整一个即可 
	if(deep[x] > deep[y]) swap(x,y);
	// 从大到小(根据二进制的性质) 
	for(int i = t; i >= 0; i --) {
		if(deep[dp[y][i]] >= deep[x]) {
			y = dp[y][i];
		}  
	}
	// 相等时即 x 本身 
	if(x == y) return x;
	for(int i = t; i >= 0; i --) {
		// 从大到小,找最近的 
		if(dp[x][i] != dp[y][i]) {
			x = dp[x][i];
			y = dp[y][i];
		}
	}
	return dp[x][0];
}

int main(void) {
	scanf("%d%d",&n,&m);
	for(int i = 1; i < n; i ++) {
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	// 树的深度 
	t = (int)(log(n) / log(2)) + 1;
	BFS();
	
	while(m --) {
		int maxDeep = -1,maxValue = 0;
		scanf("%d",&k);
		for(int i = 1; i <= k; i ++) {
			scanf("%d",&value[i]);
			// 寻找最深的点,并且保留其值 
			if(maxDeep < deep[value[i]]) {
				maxDeep = deep[value[i]];
				maxValue = value[i];
			}
		}
		bool flag = true;
		for(int i = 1; i <= k; i ++) {
			int temp = LCA(maxValue,value[i]);
			// 在同一条链上的 LCA 一定是较浅的点本身
			// 较浅的点的父节点和它 相差 1  
			if(temp != value[i] && dp[value[i]][0] != temp) {
				flag = false;
				break;
			}
		}
		if(flag) puts("YES");
		else puts("NO");
	}

	return 0;
}

后记:

通过这道题,对 LCA 有了更清楚的认识,同时,也回顾了一下
LCA 的知识。

相关链接:

大佬的 LCA 讲解

posted @ 2020-05-03 19:44  IceSwords  阅读(146)  评论(0编辑  收藏  举报