原题链接:老管家的忠诚2
解题思路: 代码分为线段树构建(build_tree)、区间查询(query)、单点更新(update)和主函数(main)四部分
注意事项:
参考代码:
#include<bits/stdc++.h> using namespace std; // 线段树空间大小:根据线段树特性,需为原始数组的4倍以上以避免越界 const int N = 4e5 + 10; int t[N]; // 线段树数组,每个节点存储对应区间的最小值 int a[N]; // 原始数据数组 int n, m; // n:原始数组长度;m:操作次数 int p, x, y; // p:操作类型(1表示查询,其他表示更新);x,y:操作参数 /** * 构建线段树 * @param pt 当前节点编号(根节点为1,左孩子为2*pt,右孩子为2*pt+1) * @param l 当前节点负责的区间左端点 * @param r 当前节点负责的区间右端点 * @return 当前节点区间的最小值(供父节点计算) */ int build_tree(int pt, int l, int r){ // 叶子节点:区间长度为1,直接存储原始数组的值 if(l == r){ t[pt] = a[l]; return a[l]; } // 非叶子节点:分裂为左右子树并递归构建 int mid = (l + r) / 2; // 计算区间中点,分割左右子树 int lm = build_tree(2 * pt, l, mid); // 构建左子树,返回左区间最小值 int rm = build_tree(2 * pt + 1, mid + 1, r); // 构建右子树,返回右区间最小值 t[pt] = min(lm, rm); // 当前节点值为左右子树最小值的较小者 return t[pt]; // 返回当前节点值,供父节点计算 } /** * 区间最小值查询 * @param pt 当前节点编号 * @param start 当前节点负责的区间左端点 * @param end 当前节点负责的区间右端点 * @param l 目标查询区间的左端点 * @param r 目标查询区间的右端点 * @return 目标区间[l, r]的最小值 */ int query(int pt, int start, int end, int l, int r){ // 情况1:当前节点区间与查询区间完全不重叠,返回极大值(不影响最小值计算) if(l > end || r < start) return 0x3f3f3f3f; // 情况2:当前节点区间完全被查询区间包含,直接返回当前节点的值 if(l <= start && r >= end) return t[pt]; // 情况3:部分重叠,递归查询左右子树 int mid = (start + end) / 2; // 基于当前节点区间计算中点 int left_min = query(2 * pt, start, mid, l, r); // 查询左子树区间 int right_min = query(2 * pt + 1, mid + 1, end, l, r); // 查询右子树区间 return min(left_min, right_min); // 返回左右子树结果的最小值 } /** * 单点更新:将原始数组x位置的值改为y,并同步更新线段树 * @param x 要更新的位置(原始数组下标) * @param y 新值 */ void update(int x, int y){ a[x] = y; // 先更新原始数组 int pt = 1; // 从根节点开始查找目标叶子节点 int s = 1, e = n; // 当前节点覆盖的区间(初始为根节点的[1, n]) // 步骤1:定位到x对应的叶子节点 while(s != e){ int mid = (s + e) / 2; if(x <= mid){ // x在左子树区间内 pt = 2 * pt; // 移动到左孩子节点 e = mid; // 更新当前区间为左子树区间[start, mid] } else { // x在右子树区间内 pt = 2 * pt + 1; // 移动到右孩子节点 s = mid + 1; // 更新当前区间为右子树区间[mid+1, end] } } t[pt] = y; // 更新叶子节点的值 // 步骤2:向上更新所有祖先节点(从父节点到根节点) while(pt > 1){ pt /= 2; // 移动到父节点 t[pt] = min(t[2 * pt], t[2 * pt + 1]); // 父节点值为左右孩子的最小值 } } int main(){ cin >> n >> m; // 输入数组长度n和操作次数m for(int i = 1; i <= n ; ++ i) // 输入原始数组元素 scanf("%d", &a[i]); build_tree(1, 1, n); // 构建线段树(根节点编号1,覆盖区间[1, n]) // 处理m次操作 while(m --){ scanf("%d %d %d", &p, &x, &y); // 读取操作类型和参数(优化为scanf提高效率) if(p == 1){ // 操作1:查询[x, y]区间的最小值 int ans = query(1, 1, n, x, y); // 从根节点开始查询 printf("%d ", ans); } else { // 其他操作:将位置x的值更新为y update(x, y); } } return 0; }
0.0分
0 人评分
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程
发表评论 取消回复