【PTA数据结构 | C语言版】线段树的操作

本专栏持续输出数据结构题目集,欢迎订阅。

文章目录

题目

请编写程序,实现线段树的四个重要操作:初始化、点更新、区间查询、区间更新。这里的查询任务定义为求最小值。

输入格式:
输入首先给出一个正整数 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值