算法竞赛进阶指南---0x43(线段树) 你能回答这些问题吗

题面

在这里插入图片描述

输出样例

5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2

输入样例

2
-1

题解

  1. 单点修改,区间查询,经典线段树问题,一般单点修改不会用到懒标记(pushdown操作),问题就会简单点
  1. 我们做线段树的题一般先确定节点中的属性,然后再做,那么节点中的属性有哪些,最基本的就是区间的左右端点 l , r 。 还有就是题中要求的最大连续字段和 lmax 。有了这三个,我们就要考虑能不能由子节点来更新出父节点的 lmax ,显然是不行的, 看图对于不跨区间的情况,我们只需要在左右儿子的lmax中去一个最大就好,但是对于跨区间来说,父节点的lmax应该是左儿子的最大后缀和+右儿子的最大前缀和
    在这里插入图片描述
  1. 那么我们的节点属性就应该增加permax,sufmax ,那么对于新加的属性,我们能否通过子节点来更新呢,也需要分情况,如图所示,我们就又加入了属性sum(区间总和),对于父节点的区间总和,就等于左右儿子的区间总和,所以我们不需要再新加属性了,当然最大后缀也是一样的道理,这里就不说明了,到此我们节点的属性就全部更新完毕了。
    在这里插入图片描述
  1. 只要确定了节点中的属性,其余我们就直接套模板就好,具体看码

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 5e5 + 10;

int n, m;
int w[N];

struct Node {
    
    
    int l, r;       //左右端点
    int tmax;      //最大连续子段和
    int premax;   //最大前缀和
    int sufmax;  //最大后缀和
    int sum;    //区间和
} tr[N * 4];

//更新节点信息的函数
void pushup(Node &u, Node &l, Node &r) {
    
    
    u.sum = l.sum + r.sum;
    u.premax = max(l.premax, l.sum + r.premax);
    u.sufmax = max(r.sufmax, r.sum + l.sufmax);
    u.tmax = max(max(l.tmax, r.tmax), l.sufmax + r.premax);
}

//由子节点更新父节点的信息
void pushup(int u) {
    
    
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

//建树
void build(int u, int l, int r) {
    
    
    if (l == r) tr[u] = {
    
    l, r, w[r], w[r], w[r], w[r]};
    else {
    
    
        tr[u] = {
    
    l, r};
        int mid = (l + r) >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

//单点修改
void modify(int u, int x, int v) {
    
    
    if (tr[u].l == x && tr[u].r == x) tr[u] = {
    
    x, x, v, v, v, v};
    else {
    
    
        int mid = (tr[u].l + tr[u].r) >> 1;
        if (x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        pushup(u);
    }
}


//区间查询
Node query(int u, int l, int r) {
    
    
    if (l <= tr[u].l && tr[u].r <= r) return tr[u];
    else {
    
    
        int mid = (tr[u].l + tr[u].r) >> 1;
        if (r <= mid) return query(u << 1, l, r);
        else if (l > mid) return query(u << 1 | 1, l, r);
        else {
    
    
            Node res;
            auto left = query(u << 1, l, r);
            auto right = query(u << 1 | 1, l, r);
            pushup(res, left, right);
            return res;
        }
    }
}

int main() {
    
    

    cin >> n >> m;
    for (int i = 1; i <= n; i++) scanf("%d", &w[i]);

    build(1, 1, n);

    while (m--) {
    
    
        int op, x, y;
        cin >> op >> x >> y;
        if (op == 1) {
    
    
            if (x > y) swap(x, y);
            cout << query(1, x, y).tmax << endl;
        } else {
    
    
            modify(1, x, y);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/113827997