Submission #1207920

#TimeUsernameProblemLanguageResultExecution timeMemory
1207920teslerphamSjeckanje (COCI21_sjeckanje)C++20
110 / 110
464 ms37828 KiB
/*
   Sjeckanje  –  COI 2021 R5
   -------------------------------------------------
   Maximum value after range-add updates.

   Key facts
   ----------
   *  Let d[i] = a[i+1] – a[i]  (1 ≤ i ≤ n-1).
   *  Best chopping value = Σ |d[i]|  minus those |d[i]| that we
      decide to “cut away”.
   *  Two consecutive differences of *opposite* sign may **not**
      be kept simultaneously (otherwise the segment would
      contain both an up-step and a down-step → not monotone).

   ↳  Choose a subset of indices that maximises
      Σ |d[i]| subject to:
        if we keep d[i] and d[i+1] then  sign(d[i])·sign(d[i+1]) ≠ −1

   We maintain a segment tree.  
   Each node keeps the best sum for the four possibilities
   (keep / drop the first diff, keep / drop the last diff).

   Node fields
   -----------
   - val[2][2] :  maximum sum for
                 val[lf][rg] where
                   lf = 1 if the first diff of the segment is kept
                   rg = 1 if the last  diff of the segment is kept
   - sL, sR    :  sign of first / last diff in the segment
                  (-1, 0, +1)

   Merge rule
   ----------
   For every combination of (lf1, rg1) from left child
   and (lf2, rg2) from right child:

        * invalid if  rg1 = 1  &&  lf2 = 1
                      && signs are opposite (+1 vs −1)

        * otherwise  new-left  = lf1
                     new-right = rg2
                     val = sum of the two children

   Complexity
   ----------
   Build :  O(n)
   Update:  O(log n)   (only two point updates per query)
   Answer :  O(1)

   -------------------------------------------------
*/

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll NEG_INF = -(1LL << 60);

struct Node {
    ll val[2][2];          // val[keepLeft][keepRight]
    int sL, sR;            // sign of first / last diff

    Node() {
        for (int i = 0; i < 2; ++i)
            for (int j = 0; j < 2; ++j)
                val[i][j] = NEG_INF;
        sL = sR = 0;
    }
};

inline int sgn(ll x) { return (x > 0) - (x < 0); }

Node merge(const Node& A, const Node& B) {
    if (A.val[0][0] == NEG_INF) return B;
    if (B.val[0][0] == NEG_INF) return A;

    Node C;
    C.sL = A.sL;
    C.sR = B.sR;

    for (int al = 0; al <= 1; ++al)
        for (int ar = 0; ar <= 1; ++ar)
            if (A.val[al][ar] != NEG_INF)
                for (int bl = 0; bl <= 1; ++bl)
                    for (int br = 0; br <= 1; ++br)
                        if (B.val[bl][br] != NEG_INF) {
                            if (ar && bl) {
                                // two consecutive kept diffs – must not have opposite signs
                                int s1 = A.sR, s2 = B.sL;
                                if (s1 != 0 && s2 != 0 && s1 != s2) continue;
                            }
                            ll cand = A.val[al][ar] + B.val[bl][br];
                            int nl = al;
                            int nr = br;
                            C.val[nl][nr] = max(C.val[nl][nr], cand);
                        }
    return C;
}

struct SegTree {
    int n;                   // number of diffs (n = N-1)
    vector<Node> st;         // 4*n
    SegTree(int _n = 0) { init(_n); }

    void init(int _n) {
        n = _n;
        st.assign(4 * max(1, n), Node());
    }

    Node make_leaf(ll diff) {
        Node nd;
        nd.sL = nd.sR = sgn(diff);
        nd.val[0][0] = 0;
        nd.val[1][1] = llabs(diff);        // keeping the only diff
        // impossible mixed states stay NEG_INF
        return nd;
    }

    void build(int p, int l, int r, const vector<ll>& diff) {
        if (l == r) {
            st[p] = make_leaf(diff[l]);
            return;
        }
        int mid = (l + r) >> 1;
        build(p << 1, l, mid, diff);
        build(p << 1 | 1, mid + 1, r, diff);
        st[p] = merge(st[p << 1], st[p << 1 | 1]);
    }

    void build(const vector<ll>& diff) {             // diff is 1-based of size n
        if (n == 0) return;
        build(1, 1, n, diff);
    }

    // point update: position idx gets new diff value 'val'
    void update(int p, int l, int r, int idx, ll val) {
        if (l == r) {
            st[p] = make_leaf(val);
            return;
        }
        int mid = (l + r) >> 1;
        if (idx <= mid) update(p << 1, l, mid, idx, val);
        else            update(p << 1 | 1, mid + 1, r, idx, val);
        st[p] = merge(st[p << 1], st[p << 1 | 1]);
    }
    void update(int idx, ll val) {                    // idx is 1-based in diff array
        if (n == 0) return;
        update(1, 1, n, idx, val);
    }

    ll best() const {
        if (n == 0) return 0LL;
        const Node& rt = st[1];
        ll ans = 0;
        for (int i = 0; i <= 1; ++i)
            for (int j = 0; j <= 1; ++j)
                ans = max(ans, rt.val[i][j]);
        return ans;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N, Q;
    if (!(cin >> N >> Q)) return 0;
    vector<ll> a(N + 1);
    for (int i = 1; i <= N; ++i) cin >> a[i];

    int M = max(0, N - 1);            // number of diffs
    vector<ll> diff(M + 1);           // 1-based
    for (int i = 1; i <= M; ++i) diff[i] = a[i + 1] - a[i];

    SegTree seg(M);
    if (M) seg.build(diff);

    while (Q--) {
        int l, r;
        ll x;
        cin >> l >> r >> x;

        if (l > 1) {
            int idx = l - 1;
            diff[idx] += x;           // d[l-1] increases by x
            seg.update(idx, diff[idx]);
        }
        if (r < N) {
            int idx = r;
            diff[idx] -= x;           // d[r] decreases by x
            seg.update(idx, diff[idx]);
        }
        cout << seg.best() << '\n';
    }
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...