P4513 小白逛公园 区间合并

解题思路

这道题需要维护一个动态变化的数组,并支持两种操作:修改某个元素的值和查询区间内最大连续子段和。使用线段树可以高效解决这个问题。

关键点:

  1. 线段树节点设计:每个节点需要维护四个信息:

    • maxx:当前区间的最大子段和

    • sum:当前区间的总和

    • ls:当前区间从左端点开始的最大子段和

    • rs:当前区间从右端点开始的最大子段和

  2. 信息合并:合并左右子区间的信息时需要考虑多种情况:

    • 最大子段和可能在左子区间、右子区间或跨越两个子区间

    • 从左端点开始的最大子段和可能是左子区间的ls,也可能是左子区间的sum加上右子区间的ls

    • 从右端点开始的最大子段和同理

  3. 查询处理:查询时需要合并多个区间的信息,处理方式与pushup类似

代码注释

#include<bits/stdc++.h>
#define f(i,s,e) for(int i = s; i <= e; i++) // 定义循环宏
#define lc rt << 1      // 左子节点索引
#define rc rt << 1 | 1   // 右子节点索引
#define lson lc,l,mid    // 左子树参数
#define rson rc,mid + 1,r // 右子树参数
#define ll long long     // 定义长整型别名
using namespace std;
const ll N = 1e6+10;
const int inf = 1e18;

// 线段树节点结构体
struct node {
    ll maxx; // 当前区间的最大子段和
    ll sum;  // 当前区间的总和
    ll ls;   // 从左端点开始的最大子段和
    ll rs;   // 从右端点开始的最大子段和
};
node t[N << 2]; // 线段树数组
ll a[N], n, m;  // 原始数组、元素个数、操作次数

// 更新当前节点的信息
void pushup(int rt) {
    t[rt].sum = t[lc].sum + t[rc].sum; // 总和是左右子树和相加
    t[rt].ls = max(t[lc].ls, t[lc].sum + t[rc].ls); // 左端最大和可能是左子树的ls或左子树sum+右子树ls
    t[rt].rs = max(t[rc].rs, t[rc].sum + t[lc].rs); // 右端最大和同理
    t[rt].maxx = max(t[lc].rs + t[rc].ls, max(t[lc].maxx, t[rc].maxx)); // 最大子段和可能是跨越左右子树或单独在某一侧
}

// 构建线段树
void build(int rt, int l, int r) {
    if(l == r) { // 叶子节点
        t[rt].maxx = t[rt].sum = t[rt].ls = t[rt].rs = a[l]; // 初始化所有值为数组元素
        return;
    }
    int mid = (l + r) >> 1;
    build(lson); // 构建左子树
    build(rson); // 构建右子树
    pushup(rt);  // 更新当前节点信息
}

// 单点修改
void change(int rt, int l, int r, int x, int y) {
    if(l == r) { // 找到目标位置
        t[rt].maxx = t[rt].sum = t[rt].ls = t[rt].rs = y; // 更新所有值
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) change(lson, x, y); // 在左子树中修改
    if(x > mid) change(rson, x, y);  // 在右子树中修改
    pushup(rt); // 更新当前节点信息
}

// 区间查询
node query(int rt, int l, int r, int x, int y) {
    if(y < l || r < x) return {-inf, -inf, -inf, -inf}; // 区间无交集,返回无效节点
    if(x <= l && r <= y) return t[rt]; // 当前区间完全包含在查询区间内
    int mid = (l + r) >> 1;
    node L = query(lson, x, y); // 查询左子树
    node R = query(rson, x, y); // 查询右子树
    node res;
    res.sum = L.sum + R.sum; // 合并总和
    res.ls = max(L.ls, L.sum + R.ls); // 合并左端最大和
    res.rs = max(R.rs, R.sum + L.rs); // 合并右端最大和
    res.maxx = max(L.rs + R.ls, max(L.maxx, R.maxx)); // 合并最大子段和
    return res;
}

int main() {
    scanf("%d%d", &n, &m);
    f(i,1,n) scanf("%lld", &a[i]); // 读取初始数组
    build(1,1,n); // 构建线段树
    
    ll op, x, y, z;
    while(m--) {
        scanf("%lld", &op);
        if(op == 2) { // 修改操作
            scanf("%lld%lld", &x, &y);
            change(1,1,n,x,y); // 修改指定位置的值
        }
        else { // 查询操作
            scanf("%lld%lld", &x, &y);
            if(x > y) swap(x,y); // 处理a>b的情况
            printf("%lld\n", query(1,1,n,x,y).maxx); // 查询并输出最大子段和
        }
    }
    return 0;
}

 

posted @ 2025-06-08 09:19  CRt0729  阅读(20)  评论(0)    收藏  举报