[每日一题]: 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 的知识。
相关链接:
如果说年轻人未来是一场盛宴的话,那么我首先要有赴宴的资格。