2023年GESP六级工作沟通(洛谷P10109):LCA问题实战解析

贾蔷
• 阅读 34

2023年GESP六级工作沟通(洛谷P10109):LCA问题实战解析

一、问题理解与建模

这道题目将公司层级关系抽象为树结构,每个员工是树中的一个节点,直接领导关系构成父子关系。我们需要解决的问题是:给定一组员工,找到能够管理所有这些员工的最低层级领导(即编号最大的最近公共祖先)。

二、算法核心:LCA的二进制提升法

二进制提升法是一种高效的LCA查询算法,主要分为两个阶段:

  1. 预处理阶段
  2. 查询阶段
    • 先将两个节点提升到同一深度
    • 然后同时向上提升直到找到共同祖先

三、实现详解

  1. 数据预处理
    • 构建树结构并存储每个节点的子节点
    • 使用DFS计算每个节点的深度
    • 预处理每个节点的2^k级祖先
  2. LCA查询
    • lift函数将节点提升到指定深度
    • lca函数实现两个节点的LCA查询
  3. 多节点处理
    • 将多个节点的LCA问题转化为连续的成对LCA查询
    • 初始LCA设为第一个节点,然后依次与其他节点求LCA

四、完整代码

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

const int MAXN = 1e5 + 5;
const int LOG = 20;

vector<int> parent(MAXN);
vector<vector<int>> up(MAXN, vector<int>(LOG));
vector<int> depth(MAXN);

// 预处理每个节点的2^k级祖先
void preprocess(int n) {
    up[0][0] = 0; // 老板的父节点是自己
    for(int i = 1; i < n; ++i) {
        up[i][0] = parent[i];
    }

    for(int k = 1; k < LOG; ++k) {
        for(int i = 0; i < n; ++i) {
            up[i][k] = up[up[i][k-1]][k-1];
        }
    }
}

// 计算节点u的深度
void dfs(int u, int p, const vector<vector<int>>& children) {
    for(int v : children[u]) {
        if(v != p) {
            depth[v] = depth[u] + 1;
            dfs(v, u, children);
        }
    }
}

// 提升节点u到指定深度
int lift(int u, int target_depth) {
    for(int k = LOG-1; k >= 0; --k) {
        if(depth[u] - (1 << k) >= target_depth) {
            u = up[u][k];
        }
    }
    return u;
}

// 查找两个节点的LCA
int lca(int u, int v) {
    if(depth[u] < depth[v]) swap(u, v);
    u = lift(u, depth[v]);

    if(u == v) return u;

    for(int k = LOG-1; k >= 0; --k) {
        if(up[u][k] != up[v][k]) {
            u = up[u][k];
            v = up[v][k];
        }
    }
    return parent[u];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    cin >> N;

    vector<vector<int>> children(N);
    parent[0] = 0;
    for(int i = 1; i < N; ++i) {
        cin >> parent[i];
        children[parent[i]].push_back(i);
    }

    // 预处理
    preprocess(N);
    depth[0] = 0;
    dfs(0, -1, children);

    int Q;
    cin >> Q;
    while(Q--) {
        int m;
        cin >> m;
        vector<int> group(m);
        for(int i = 0; i < m; ++i) {
            cin >> group[i];
        }

        // 找到所有节点的LCA
        int current_lca = group[0];
        for(int i = 1; i < m; ++i) {
            current_lca = lca(current_lca, group[i]);
            if(current_lca == 0) break; // 已经到根节点
        }

        cout << current_lca << '\n';
    }

    return 0;
}

来源:2023年GESP六级工作沟通(洛谷P10109):LCA问题实战解析

点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
LeetCode 235. 二叉搜索树的最近公共祖先
原文链接: LeetCode235.二叉搜索树的最近公共祖先(https://my.oschina.net/ahaoboy/blog/3118052)给定一个二叉搜索树,找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树T的两个结点p、q,最近公共祖先表示为一个结点x,满足x是p、q的祖先且x
贾蔷 贾蔷
1个月前
2025年GESP七级等价消除(洛谷P11965)代码解析与优化策略
一、题目解读2025年GESP七级考试中的“等价消除(洛谷P11965)”问题要求统计给定字符串中满足等价条件的子串数量。所谓“等价子串”,是指子串中所有字符出现的次数均相同。题目需要高效算法解决,考验对字符串处理和状态压缩的掌握。二、解题思路采用位运算
贾蔷 贾蔷
1星期前
2023年GESP六级小杨握手问题(洛谷B3874):Fenwick树求解逆序对的代码解析
一、题目解读“小杨的握手问题”源自2023年GESP六级考试(对应洛谷题目B3874)。题目描述为:给定一个长度为N的排列,每次将当前数与之前未访问过的数握手,求总共握手次数。本质上是求排列中逆序对的个数,即统计每个数右侧比它小的元素数量。需设计高效算法在
贾蔷 贾蔷
1星期前
【蓝桥杯2015省赛解析】生命之树(洛谷P8625):树形DP解题全攻略
一、题目解读“生命之树”是一道经典的树形结构问题,要求计算一棵带权树中,以某个节点为根的最大子树权值和。题目输入为n个节点及边信息,每个节点有权值wi,需找到所有节点中,子树权值和最大的节点,并输出其值。核心挑战在于如何处理树形结构的递归关系,并高效聚合子
深度学习 深度学习
1星期前
【CSP-S 2019】括号树(洛谷P5658):栈+DFS
一、题目解读括号树问题(洛谷P5658)要求处理一个由括号序列转化的树结构:每个节点表示一个括号,'('为子节点,')'为父节点。题目给定一棵n个节点的树,需计算每个节点的深度(括号层数),并输出所有节点深度与节点编号乘积的异或和。核心在于将括号序列转化为
深度学习 深度学习
1星期前
洛谷1111题解:基于Kruskal算法与并查集的最小生成树实现
一、题目解读洛谷1111题是一道经典的图论问题,要求构建一个无向图的最小生成树,并输出其最大边权值。题目核心在于通过给定的边集合,找到连接所有节点的最小权值子集,同时保证无环。这通常涉及最小生成树算法(如Kruskal)的应用,需要高效处理边权重与节点连通
贾蔷 贾蔷
1星期前
牛客13279题解:利用递归与深度优先搜索计算树的最大高度(附完整代码)
一、题目解读牛客13279题要求计算给定树的最大高度。题目输入一棵以邻接表形式表示的树(节点从0开始编号),需要输出从根节点到最深叶节点的最长路径长度。树的结构由n个节点和n1条边构成,保证为连通无环图。理解题目核心在于准确获取树的拓扑关系,并设计算法遍历
贾蔷 贾蔷
1星期前
2023年GESP六级题解:洛谷P10108闯关游戏动态规划解法详解
一、题目解读本文针对2023年GESP六级题目“闯关游戏”(洛谷P10108)进行详细解析。题目要求玩家通过不同关卡路径选择,计算从起点到终点的最大得分。关卡间存在跳跃规则,需结合动态规划思想设计高效算法,最终输出最优得分。二、解题思路采用动态规划(Dyn
深度学习 深度学习
1星期前
洛谷P2034题解:动态规划+单调队列优化求解最大K段子段和问题
一、题目解读洛谷P2034题目要求给定一个长度为n的整数数组,将其分成不超过k段,求各段和的最大值。该问题属于经典动态规划问题的扩展,需结合优化技巧高效求解。二、解题思路采用动态规划单调队列优化的策略。核心思想是定义状态dp
贾蔷 贾蔷
3天前
牛客3750题滑动窗口最大值解析:双端队列优化解法与代码详解
一、题目解读要求在一个给定的整数中,计算固定大小为k的内元素的最大值。例如,当窗口滑动时,需要实时输出每个窗口中的最大值序列。该问题考察对的理解,以及如何高效维护窗口内的元素关系。二、解题思路采用(deque)维护的巧妙解法。核心思想是:中仅存储数组下标,