• [每日一题]: 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 讲解

  • 相关阅读:
    [LeetCode]62. Excel Sheet Column Title Excel列序号
    [LeetCode]61. Excel Sheet Column Number Excel列序号
    [LeetCode]60. Rectangle Area矩形面积
    [LeetCode]59. H-Index H指数
    [LeetCode]58. Fraction to Recurring Decimal分数化小数
    [LeetCode]57. Binary Tree Inorder Traversal中序遍历二叉树
    Insert or Merge
    Root of AVL Tree
    是否同一棵二叉搜索树
    List Leaves
  • 原文地址:https://www.cnblogs.com/prjruckyone/p/12823115.html
Copyright © 2020-2023  润新知