当前位置:首页 > GESP > 洛谷P10113题(2023年GESP八级):用LCA算法高效解决大量的工作沟通

洛谷P10113题(2023年GESP八级):用LCA算法高效解决大量的工作沟通

7小时前

洛谷P10113题(2023年GESP八级):用LCA算法高效解决大量的工作沟通 LCA算法 树结构 GESP GESP八级 ST表 二分查找 递归 第1张

一、题目解读

洛谷P10113题(2023年GESP八级)要求解决树结构中多个节点的最近公共祖先(LCA)查询问题。给定一棵及若干组节点,需快速找出每组节点的LCA。传统方法如暴力遍历时间复杂度较高,而本题采用倍增法(RMQ算法)优化,通过预处理实现O(logN)的查询效率,适用于大规模数据场景。

二、解题思路

核心是倍增法(基于ST表思想的变体),通过构建“倍增表”预处理每个节点的祖先信息。关键在于利用二进制拆分思想:

1. 预处理阶段:计算每个节点深度,构建up[i][j]表表示节点i的2^j层祖先。

2. 查询阶段:通过二分提升深度差异,再同步向上移动找到LCA。

此思路将单次查询复杂度降至O(logN),大幅优化性能。

三、解题步骤

1. 预处理阶段

● 初始化根节点(0号节点)深度为0,父节点为自身。

递归计算子节点深度:depth[i] = depth[parent[i]] + 1。

● 填充倍增表:up[i][j] = up[up[i][j-1]][j-1],即节点i的2^j祖先通过其2^(j-1)祖先的2^(j-1)祖先间接获得。

2. LCA查询

● 确保较深节点u与v深度对齐:通过二进制拆分快速提升u至与v同深度。

● 同步向上:从最高位开始,若u、v的2^j祖先不同,则同时向父节点移动。最终两者父节点即为LCA。

四、代码与注释

#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) {  
    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) {  
    if(depth[u] < depth[v]) swap(u, v); // 确保u更深  
    for(int j = LOG-1; j >= 0; --j) {  
        if(depth[u] - (1 << j) >= depth[v]) {  
            u = up[u][j]; // 提升u  
        }  
    }  
    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]; // 最终LCA为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';  
    }  
}

注释说明:

● up[i][j] 存储节点i的2^j层祖先,预处理通过迭代构建。

● lca(u, v) 函数通过二分法对齐深度,再同步向上查找LCA。

五、总结

本文通过洛谷P10113题的代码解析,展示了倍增法在LCA问题中的高效应用。该算法通过预处理空间换取查询时间,尤其适用于需要频繁查询LCA的场景。代码结构清晰,预处理与查询分离,可扩展性强。理解此解法有助于优化树结构相关算法设计,提升解题效率。


原创内容 转载请注明出处

分享给朋友:

相关文章

力扣第654题:最大二叉树解题教程 用数组构造最大二叉树

力扣第654题:最大二叉树解题教程 用数组构造最大二叉树

题目解读给定一个不含重复元素的整数数组,我们需要构建一棵最大二叉树。构建规则是:数组中的最大值作为根节点,其左侧子数组构建左子树,右侧子数组构建右子树,然后递归地应用这个规则。这种构建方式体现了分治思...

【力扣2846题】图论+二进制提升:高效解决连通性问题(附C++代码)

【力扣2846题】图论+二进制提升:高效解决连通性问题(附C++代码)

一、题目解读力扣2846题要求解决一个基于图连通性的操作优化问题。给定一个无向图,包含边权重,以及一系列查询,每个查询询问两点间路径的最小操作次数。题目关键在于高效计算路径上权重分布的统计信息,并转化...

牛客13279题解:基于广度优先搜索(BFS)计算树高度的算法优化与代码实现

牛客13279题解:基于广度优先搜索(BFS)计算树高度的算法优化与代码实现

一、题目解读牛客13279题要求计算给定树的高度。树由n个节点组成,通过n-1条边连接,形成一棵有根树。题目核心在于高效求解树的高度,即从根节点到最深叶子节点的最长路径长度。需要理解树的结构特点,选择...

【牛客4456题解析】最长上升子序列的动态规划+二分查找解法

【牛客4456题解析】最长上升子序列的动态规划+二分查找解法

一、题目解读牛客4456题要求在一个整数序列中寻找最长上升子序列(LIS)的长度。题目考察的核心是动态规划与高效查找算法的结合,需要设计一种能在给定序列中快速找到最长递增子序列的方法,通常需要平衡时间...

洛谷P2420题解析:树结构异或路径的高效求解算法

洛谷P2420题解析:树结构异或路径的高效求解算法

一、题目解读洛谷P2420题要求处理一棵树上的路径异或问题。给定一棵包含N个节点的树,每条边有权值,需回答M次查询:求节点u到节点v路径上所有边权值的异或和。题目关键在于如何高效利用树结构和异或运算的...

牛客4485题解题指南:最短子序列问题的优化解法与代码解析

牛客4485题解题指南:最短子序列问题的优化解法与代码解析

一、题目解读牛客4485题要求在一个整数数组中找到最短的子序列长度,该子序列需包含原数组的所有“转折点”(即单调递增或递减的区间边界)。题目关键在于识别转折点并确定最小覆盖范围,需平衡时间与空间效率。...

发表评论

访客

看不清,换一张

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