Logu P4513: segment tree maintenance dynamic interval maximum sub segment sum

Pre knowledge

Segment tree

Segment tree is a binary search tree, which is similar to interval tree. It divides an interval into some unit intervals, and each unit interval corresponds to a leaf node in the segment tree.

Using the segment tree, you can quickly find the number of occurrences of a node in several segments. The time complexity is \ (O(log\ n) \).

If you don't know the line segment tree, it is recommended to start with some simple topics, such as Luogu P3372 [template] segment tree 1.

Maximum sub segment sum

The maximum sub segment sum is defined as the sequence \ (a_1,a_2,a_3,\ldots,a_n \) composed of given \ (n \) integers (possibly negative numbers), and the maximum value of the sub segment sum of the sequence such as \ (a_i,a_{i+1},\ldots,a_j \).

dp or recursion are generally used to solve such problems.

meaning of the title

Title Link

Given a sequence \ (a_1,a_2,\ldots,a_n \), two operations are supported:

Operation 1: find the maximum sub segment sum of interval \ ([l,r] \).

Action 2: change \ (a_x \) to \ (k \).

solution

Firstly, it can be found that the maximum sub segment sum satisfies interval addition, so we can think of using segment tree to maintain.

However, interval addition cannot be directly used in this problem. For example, the maximum sum of sub segments of \ (\ {1, - 1 \} \) is \ (1 \), and the maximum sum of sub segments of \ (\ {- 1,1 \} \) is also \ (1 \), but the maximum sum of sub segments of \ (\ {1, - 1, - 1,1 \} \) is \ (1 \), not directly \ (1 + 1 = 2 \).

We need special maintenance methods.

Through observation, it can be found that for an interval with a length greater than \ (1 \), the maximum sub segments and intervals of its two sons may not be continuous. If you want to add the maximum sub segments and intervals of the two sons, you must make the maximum sub segments and intervals of the left son to the right and the maximum sub segments and intervals of the right son to the left. For example, \ (\ {- 1,1 \} \) and \ (\ {1, - 1 \} \) can use interval addition, The maximum sub segment sum of \ (\ {- 1,1,1, - 1 \} \) is \ (1 + 1 = 2 \).

Therefore, we need to maintain the interval and \ (pre \), the maximum sub segment and \ (ans \), the maximum sub segment and \ (ml \) that must be left, and the maximum sub segment and \ (mr \) that must be right.

The interval sum is relatively simple. You only need to add up the left son and the right son, so you can get:

\(pre_i=pre_{2i}+pre_{2i+1}\)

The largest sub segment that must be left and can be transferred from two states. The first is \ (ML {2I} \), which means that only the left part of the left son is selected, the second is \ (pre {2I} + ml {2I + 1} \), which means that all the left sons are selected, and the right son takes the largest part to the left, so you can get:

\(ml_i=\max(ml_{2i},pre_{2i}+ml_{2i+1})\)

The largest sub segment that must be on the right is the same as:

\(mr_i=\max(mr_{2i+1},pre_{2i+1}+mr_{2i})\)

The maximum sub segment sum is transferred from three states. The first is \ (ANS {2I} \), indicating that only the optimal part of the left son is selected, the second is \ (ANS {2I + 1} \), indicating that only the optimal part of the right son is selected, and the third is \ (MR {2I} + ml {2I + 1} \), indicating that the optimal right part of the left son is taken, plus the optimal left part of the right son, so:

\(ans_i=\max(ans_{2i},ans_{2i+1},mr_{2i}+ml_{2i+1})\)

So the problem was finished.

code

#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define il inline

using namespace std;

int n, m;
int a[500005];
int opt, l, r, x, k;

#define lc (k << 1)
#define rc ((k << 1) | 1)

struct tree {
    int l, r;
    int pre, ml, mr, ans;
}t[2000005];

il void pushup (int k) {
    t[k].pre = t[lc].pre + t[rc].pre;
    t[k].ml = max(t[lc].ml, t[lc].pre + t[rc].ml);
    t[k].mr = max(t[rc].mr, t[rc].pre + t[lc].mr);
    t[k].ans = max(max(t[lc].ans, t[rc].ans), t[lc].mr + t[rc].ml);

    return;
} 

il void build (int k, int l, int r) {
    t[k].l = l, t[k].r = r;

    if (l == r) {
        t[k].pre = a[l];
        t[k].ml = a[l];
        t[k].mr = a[l];
        t[k].ans = a[l];

        return;
    }

    int mid = (l + r) >> 1;

    build(lc, l, mid);
    build(rc, mid + 1, r);

    pushup(k);
}

il void add (int k, int x, int pre) {
    if (t[k].l == t[k].r) {
        t[k].ml = pre;
        t[k].mr = pre;
        t[k].pre = pre;
        t[k].ans = pre;

        return;
    }

    int mid = (t[k].l + t[k].r) >> 1;

    if (x <= mid) {
        add(lc, x, pre);
    }

    else {
        add(rc, x, pre);
    }

    pushup(k);

    return;
}

tree ask (int k, int l, int r) {
    if (l <= t[k].l && r >= t[k].r) {
        return t[k];
    }

    int mid = (t[k].l + t[k].r) >> 1;

    if (r <= mid) {
        return ask(lc, l, r);
    }

    if (l > mid) {
        return ask(rc, l, r);
    }

    tree ls = ask(lc, l, r), rs = ask(rc, l, r), res;

    res.ml = max(ls.ml, ls.pre + rs.ml);
    res.mr = max(rs.mr, rs.pre + ls.mr);
    res.pre = ls.pre + rs.pre;
    res.ans = max(max(ls.ans, rs.ans), ls.mr + rs.ml);

    return res;
}

int main () {
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);

    cin >> n >> m;

    rep (i, 1, n) {
        cin >> a[i];
    }

    build(1, 1, n);

    rep (i, 1, m) {
        cin >> opt;

        if (opt == 1) {
            cin >> l >> r;

            if (l > r) {
                swap(l, r);
            }

            cout << ask(1, l, r).ans << endl;
        }

        if (opt == 2) {
            cin >> x >> k;

            add(1, x, k);
        }
    }

    return 0;
}

Posted by Mad_Mike on Tue, 30 Nov 2021 08:53:08 -0800