当前位置:首页 > 比赛题解 > (2023GESP八级)洛谷P10113题:用树结构解决员工管理的问题

(2023GESP八级)洛谷P10113题:用树结构解决员工管理的问题

5天前比赛题解74

截图未命名.jpg (2023GESP八级)洛谷P10113题:用树结构解决员工管理的问题 树结构 C++ 员工管理 GESP GESP八级 洛谷题解 LCA算法 第1张

一、问题分析

这道题目描述了一个公司的员工层级关系,可以抽象为一个树形结构,其中0号员工是根节点(老板)。我们需要解决的问题是:给定一组员工,找到能够管理所有这些员工的最低层级的共同上级(即编号最大的最近公共祖先)。

二、树结构基础

在计算机科学中,树是一种重要的数据结构,由节点和边组成,具有以下特点:

  1. 有一个特殊的节点称为根节点

  2. 每个节点有零个或多个子节点

  3. 除了根节点外,每个节点有且只有一个父节点

在本问题中,公司的管理结构正好形成了一棵树,其中每个员工(除老板外)都有一个直接领导(父节点)。

三、最近公共祖先(LCA)算法

最近公共祖先(Lowest Common Ancestor)是指在一棵树中,两个节点的共同祖先中离它们最近的节点。对于多个节点,可以依次计算它们的LCA。

四、解决方案

我们可以采用以下步骤解决这个问题:

  1. 预处理每个节点的深度和祖先信息

  2. 对于每组查询,找出所有节点的LCA

  3. 如果有多个可能的LCA,选择编号最大的那个

五、C++代码实现

以下是完整的C++实现代码,包含详细注释:

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

const int MAXN = 1e5 + 5;
const int LOG = 20; // 足够大的对数级别

vector<int> parent(MAXN); // 存储每个节点的直接领导
vector<int> depth(MAXN);  // 存储每个节点的深度
vector<vector<int>> up(MAXN, vector<int>(LOG)); // 倍增表

// 预处理函数,构建倍增表
void preprocess(int n) {
    // 0号节点是根节点,深度为0,没有父节点
    depth[0] = 0;
    up[0][0] = 0; // 根节点的2^0祖先还是自己
    
    // 预处理其他节点
    for(int i = 1; i < n; ++i) {
        depth[i] = depth[parent[i]] + 1;
        up[i][0] = parent[i]; // 2^0祖先就是直接领导
    }
    
    // 填充倍增表
    for(int j = 1; j < LOG; ++j) {
        for(int i = 0; i < n; ++i) {
            up[i][j] = up[up[i][j-1]][j-1];
        }
    }
}

// 查找两个节点的LCA
int lca(int u, int v) {
    // 确保u是较深的节点
    if(depth[u] < depth[v]) swap(u, v);
    
    // 将u提升到与v相同的深度
    for(int j = LOG-1; j >= 0; --j) {
        if(depth[u] - (1 << j) >= depth[v]) {
            u = up[u][j];
        }
    }
    
    // 如果此时u和v相同,则已经找到LCA
    if(u == v) return u;
    
    // 否则,同时提升u和v直到它们的父节点相同
    for(int j = LOG-1; j >= 0; --j) {
        if(up[u][j] != up[v][j]) {
            u = up[u][j];
            v = up[v][j];
        }
    }
    
    return parent[u];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N;
    cin >> N;
    
    // 读取每个员工的直接领导
    for(int i = 1; i < N; ++i) {
        cin >> parent[i];
    }
    
    // 预处理倍增表
    preprocess(N);
    
    int Q;
    cin >> Q;
    
    while(Q--) {
        int m;
        cin >> m;
        vector<int> employees(m);
        for(int i = 0; i < m; ++i) {
            cin >> employees[i];
        }
        
        // 找出所有员工的LCA
        int current_lca = employees[0];
        for(int i = 1; i < m; ++i) {
            current_lca = lca(current_lca, employees[i]);
        }
        
        cout << current_lca << "\n";
    }
    
    return 0;
}


六、代码解析

  1. 预处理阶段‌:我们使用倍增法预处理每个节点的2^j级祖先,这样可以快速跳跃查找祖先节点。

  2. LCA查找‌:对于两个节点,我们首先将它们调整到同一深度,然后同时向上跳跃查找共同祖先。

  3. 查询处理‌:对于每组查询,我们依次计算所有节点的LCA,最终结果即为所求的主持人。

希望这篇文章能帮助你理解树结构和LCA算法,并成功解决这道题目!


原创内容 转载请注明出处

分享给朋友:

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。