本专栏持续输出数据结构题目集,欢迎订阅。
题目
请编写程序,实现线段树的四个重要操作:初始化、点更新、区间查询、区间更新。这里的查询任务定义为求最小值。
输入格式:
输入首先给出一个正整数 n(2≤n≤10^3),随后一行给出 n 个绝对值不超过 10^8
的整数,为存入线段树的原始数据。接下来三行,依次给出:
- 第一次查询的区间端点位序 ql 和 qr;
- 点更新的位序 idx 和更新值 value;
- 区间更新的区间端点位序 ql 和 qr,以及该区间内所有元素的增值 c。
位序在 1 到 n 之间,更新值和增值均为绝对值不超过 10^8 的整数。
输出格式:
第一行输出第一次查询的区间 [ql,qr] 内的最小值;第二行输出将第 idx 个元素的值改为 value 后,区间 [ql,qr] 内的最小值;第三行输出将区间 [ql,qr] 内的所有元素值增加 c 以后,区间 [ql,qr] 内的最小值。每一行的格式均为:
min[a, b] = v
其中 a 和 b 为区间端点,v 为区间内最小值。
输入样例:
14
8 3 6 6 9 8 4 4 7 5 7 9 6 9
1 14
7 1
5 11 -2
输出样例:
min[1, 14] = 3
min[1, 14] = 1
min[5, 11] = -1
代码
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAX_N 1000
#define INF INT_MAX
// 线段树节点结构
typedef struct {
int min_val; // 区间最小值
int lazy; // 懒标记,用于区间更新
} SegmentTreeNode;
SegmentTreeNode tree[4 * MAX_N]; // 线段树数组,大小为4*n
int arr[MAX_N]; // 原始数组
int n; // 元素数量
// 构建线段树
void build(int node, int start, int end) {
// 初始化懒标记
tree[node].lazy = 0;
if (start == end) {
// 叶子节点,存储对应数组元素的值
tree[node].min_val = arr[start];
} else {
int mid = (start + end) / 2;
int left_child = 2 * node + 1;
int right_child = 2 * node + 2;
// 递归构建左右子树
build(left_child, start, mid);
build(right_child, mid + 1, end);
// 当前节点的最小值为左右子树最小值中的较小者
tree[node].min_val = (tree[left_child].min_val < tree[right_child].min_val) ?
tree[left_child].min_val : tree[right_child].min_val;
}
}
// 下推懒标记
void push_down(int node, int start, int end) {
if (tree[node].lazy != 0) {
int mid = (start + end) / 2;
int left_child = 2 * node + 1;
int right_child = 2 * node + 2;
// 更新左子树
tree[left_child].min_val += tree[node].lazy;
tree[left_child].lazy += tree[node].lazy;
// 更新右子树
tree[right_child].min_val += tree[node].lazy;
tree[right_child].lazy += tree[node].lazy;
// 清除当前节点的懒标记
tree[node].lazy = 0;
}
}
// 点更新:将位置idx的值更新为value
void point_update(int node, int start, int end, int idx, int value) {
if (start == end) {
// 找到目标位置,更新值
arr[idx] = value;
tree[node].min_val = value;
} else {
int mid = (start + end) / 2;
int left_child = 2 * node + 1;
int right_child = 2 * node + 2;
if (idx <= mid) {
// 目标位置在左子树
point_update(left_child, start, mid, idx, value);
} else {
// 目标位置在右子树
point_update(right_child, mid + 1, end, idx, value);
}
// 更新当前节点的最小值
tree[node].min_val = (tree[left_child].min_val < tree[right_child].min_val) ?
tree[left_child].min_val : tree[right_child].min_val;
}
}
// 区间更新:将区间[ql, qr]内的所有元素增加c
void range_update(int node, int start, int end, int ql, int qr, int c) {
if (qr < start || end < ql) {
// 区间不重叠,无需更新
return;
}
if (ql <= start && end <= qr) {
// 当前区间完全在更新区间内
tree[node].min_val += c;
tree[node].lazy += c;
return;
}
// 下推懒标记
push_down(node, start, end);
int mid = (start + end) / 2;
int left_child = 2 * node + 1;
int right_child = 2 * node + 2;
// 递归更新左右子树
range_update(left_child, start, mid, ql, qr, c);
range_update(right_child, mid + 1, end, ql, qr, c);
// 更新当前节点的最小值
tree[node].min_val = (tree[left_child].min_val < tree[right_child].min_val) ?
tree[left_child].min_val : tree[right_child].min_val;
}
// 区间查询:查询区间[ql, qr]内的最小值
int range_query(int node, int start, int end, int ql, int qr) {
if (qr < start || end < ql) {
// 区间不重叠,返回无穷大
return INF;
}
if (ql <= start && end <= qr) {
// 当前区间完全在查询区间内,返回当前节点的最小值
return tree[node].min_val;
}
// 下推懒标记
push_down(node, start, end);
int mid = (start + end) / 2;
int left_child = 2 * node + 1;
int right_child = 2 * node + 2;
// 递归查询左右子树
int left_min = range_query(left_child, start, mid, ql, qr);
int right_min = range_query(right_child, mid + 1, end, ql, qr);
// 返回左右子树查询结果中的较小者
return (left_min < right_min) ? left_min : right_min;
}
int main() {
// 读取元素数量
scanf("%d", &n);
// 读取原始数据(注意:将1-based转为0-based)
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// 构建线段树
build(0, 0, n - 1);
// 读取第一次查询的区间
int ql, qr;
scanf("%d %d", &ql, &qr);
// 转换为0-based索引
ql--; qr--;
int min1 = range_query(0, 0, n - 1, ql, qr);
printf("min[%d, %d] = %d\n", ql + 1, qr + 1, min1);
// 读取点更新信息
int idx, value;
scanf("%d %d", &idx, &value);
// 转换为0-based索引
idx--;
point_update(0, 0, n - 1, idx, value);
int min2 = range_query(0, 0, n - 1, ql, qr);
printf("min[%d, %d] = %d\n", ql + 1, qr + 1, min2);
// 读取区间更新信息
int u_ql, u_qr, c;
scanf("%d %d %d", &u_ql, &u_qr, &c);
// 转换为0-based索引
u_ql--; u_qr--;
range_update(0, 0, n - 1, u_ql, u_qr, c);
int min3 = range_query(0, 0, n - 1, u_ql, u_qr);
printf("min[%d, %d] = %d\n", u_ql + 1, u_qr + 1, min3);
return 0;
}
1370

被折叠的 条评论
为什么被折叠?



