洛谷题单指南-线段树-P4513 小白逛公园

原题链接:https://www.luogu.com.cn/problem/P4513

题意解读:给定序列a[n],支持两种操作:1.查询区间[l,r]内的最大子段和 2.将a[x]修改成s,输出其中每一个查询操作的结果。

解题思路:区间问题依然想到线段树,问题主要在于线段树的节点要维护哪些信息:

最直接的,肯定要维护节点所表示区间的最大子段和sum,

那么问题来了,节点所表示区间的最大字段和如何来根据子节点计算得来?

经过分析,一个节点区间的最大子段和与其子节点区间的最大子段和有如下关系:

所以,仅仅维护节点区间的最大子段和maxsum还不够,还需要维护节点区间的最大后缀和subsum以及最大前缀和presum,下面就要解决这三个信息如何通过子节点更新的问题

1、maxsum的更新

根据以上分析,不难发现:父节点的maxsum = max(左子节点的maxsum, 右子节点的maxsum,左子结点的subsum + 右子节点的presum)

2、presum的更新

经过分析,一个节点区间的最大前缀和与其子节点有如下关系:

所以,还需要增加一个信息:区间和sum,然后可以得到:父节点的presum = max(左子结点的presum, 左子结点的sum + 右子节点的presum)

3、subsum的更新

同理可以分析,一个节点的最大后缀和与其子节点有如下关系:

不难发现:父节点的subsum = max(右子节点的subsum, 右子节点的sum + 左子结点的subsum)

4、sum的更新

父节点的sum = 左子结点的sum + 右子节点的sum

通过维护以上信息,就可以通过线段树实现该问题,甚至不需要用到懒标记!

需要注意的是,在实现查询操作时,如果节点不在所查询的区间范围内,就不应该参与pushup计算,也就是不能影响pushup计算的结果,因此要注意将maxsum、presum、subsum初始化为-INF,INF的取值要注意取1e9比较合适,如果再大有可能在相加时导致溢出。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 500005, INF = 1e9;

struct Node 
{
    int l, r;
    int maxsum; //区间最大子段和
    int presum; //区间最大前缀和
    int subsum; //区间最大后缀和
    int sum; //区间和
} tr[N * 4];
int a[N];
int n, m;

void pushup(Node &root, Node &left, Node &right)
{
    root.sum = left.sum + right.sum;
    root.maxsum = max(max(left.maxsum, right.maxsum), left.subsum + right.presum);
    root.presum = max(left.presum, left.sum + right.presum);
    root.subsum = max(right.subsum, right.sum + left.subsum);
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if(l == r) tr[u].sum = tr[u].maxsum = tr[u].presum = tr[u].subsum = a[l]; //叶子节点的值
    else
    {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void update(int u, int x, int val)
{
    if(tr[u].l == tr[u].r) tr[u].sum = tr[u].maxsum = tr[u].presum = tr[u].subsum = val; //修改叶子节点的值
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(x <= mid) update(u << 1, x, val);
        else update(u << 1 | 1, x, val);
        pushup(u);
    }
}

Node query(int u, int l, int r)
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u];
    else if(tr[u].l > r || tr[u].r < l) return Node{0, 0, -INF, -INF, -INF, 0}; //不在范围内的节点不能影响pushup的结果,注意初始化值-INF
    else
    {
        Node left = query(u << 1, l, r);
        Node right = query(u << 1 | 1, l, r);
        Node res = {};
        pushup(res, left, right); //借助pushup函数来计算左右节点合并后的值
        return res;
    }
}

int main() 
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    build(1, 1, n);
    int op, x, y;
    while(m--) 
    {
        cin >> op >> x >> y;
        if(op == 1) 
        {
            if(x > y) swap(x, y);
            cout << query(1, x, y).maxsum << endl;
        } 
        else update(1, x ,y);
    }
    return 0;
}

 

posted @ 2024-12-02 19:29  五月江城  阅读(13)  评论(0编辑  收藏  举报