QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: Anonymous

Posted at: 2026-09-12 19:11:23

Last updated: 2026-09-12 20:14:08

Back to Problem

$O(\sqrt{k})$ by GPT6 Pro

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

using u64 = uint64_t;
using i64 = int64_t;

class FastIO {
    static constexpr size_t S = 1 << 20;
    char in[S], out[S];
    size_t ip = 0, il = 0, op = 0;

    int getChar() {
        if (ip == il) {
            il = fread(in, 1, S, stdin);
            ip = 0;
            if (!il) return EOF;
        }
        return static_cast<unsigned char>(in[ip++]);
    }
public:
    ~FastIO() { flush(); }
    bool readInt(int &x) {
        int c;
        do { c = getChar(); } while (c != EOF && c <= ' ');
        if (c == EOF) return false;
        int sign = 1;
        if (c == '-') { sign = -1; c = getChar(); }
        x = 0;
        while (c >= '0' && c <= '9') {
            x = x * 10 + c - '0';
            c = getChar();
        }
        x *= sign;
        return true;
    }
    void flush() {
        if (op) fwrite(out, 1, op, stdout);
        op = 0;
    }
    void write(u64 x) {
        char s[24];
        int k = 0;
        do { s[k++] = char('0' + x % 10); x /= 10; } while (x);
        if (op + k + 1 > S) flush();
        while (k) out[op++] = s[--k];
        out[op++] = '\n';
    }
};

// Auxiliary segment trees store the TWO prefix arrays of a main-tree interval.
// They share subtrees; reference counts + copy-on-write reclaim old profiles.
class ProfileTree {
    struct Node {
        u64 sx, sy, sxy;
        int lc, rc, refs;
        int xmin, xmax, ymin, ymax;
        int tx, ty;
        unsigned mask;  // bit 0: x <- tx; bit 1: y <- ty. Otherwise += tag.
    };
    vector<Node> t;
    int freeHead = 0;

    int alloc(const Node &value) {
        if (freeHead) {
            int p = freeHead;
            freeHead = t[p].lc;
            t[p] = value;
            return p;
        }
        t.push_back(value);
        return int(t.size()) - 1;
    }

    // The caller gives this function ownership of one reference.
    int uniqueNode(int p) {
        if (t[p].refs == 1) return p;
        Node v = t[p];
        --t[p].refs;
        v.refs = 1;
        retain(v.lc);
        retain(v.rc);
        return alloc(v);
    }

    void pull(int p) {
        Node &v = t[p];
        const Node &a = t[v.lc], &b = t[v.rc];
        v.sx = a.sx + b.sx;
        v.sy = a.sy + b.sy;
        v.sxy = a.sxy + b.sxy;
        // x is nonincreasing; y is nondecreasing.
        v.xmax = a.xmax; v.xmin = b.xmin;
        v.ymin = a.ymin; v.ymax = b.ymax;
    }

    // Product sum after x <- (setX ? 0 : x) + dx, similarly for y.
    static u64 eval(const Node &v, int len, int dx, int dy, unsigned mask) {
        u64 x = u64(i64(dx)), y = u64(i64(dy));
        u64 ans = x * y * u64(len);
        if (!(mask & 1)) ans += y * v.sx;
        if (!(mask & 2)) ans += x * v.sy;
        if (mask == 0) ans += v.sxy;
        return ans;
    }

    void apply(int p, int len, int dx, int dy, unsigned mask) {
        Node &v = t[p];
        v.sxy = eval(v, len, dx, dy, mask);
        v.sx = ((mask & 1) ? 0 : v.sx) + u64(i64(dx)) * u64(len);
        v.sy = ((mask & 2) ? 0 : v.sy) + u64(i64(dy)) * u64(len);
        if (mask & 1) {
            v.xmin = v.xmax = dx;
            v.tx = dx;
        } else {
            v.xmin += dx; v.xmax += dx; v.tx += dx;
        }
        if (mask & 2) {
            v.ymin = v.ymax = dy;
            v.ty = dy;
        } else {
            v.ymin += dy; v.ymax += dy; v.ty += dy;
        }
        v.mask |= mask;
    }

    void push(int p, int len) {
        const int dx = t[p].tx, dy = t[p].ty;
        const unsigned mask = t[p].mask;
        if (dx == 0 && dy == 0 && mask == 0) return;
        const int leftLen = (len + 1) / 2;
        int lc = uniqueNode(t[p].lc);
        int rc = uniqueNode(t[p].rc);
        // Do not keep a vector element reference across allocations.
        t[p].lc = lc; t[p].rc = rc;
        apply(lc, leftLen, dx, dy, mask);
        apply(rc, len - leftLen, dx, dy, mask);
        t[p].tx = t[p].ty = 0;
        t[p].mask = 0;
    }

    // x <- min(x, low), y <- max(y, high).
    // Only the paths to at most TWO boundary positions can be partial.
    int clip(int p, int len, int low, int high) {
        bool needX = t[p].xmax > low;
        bool needY = t[p].ymin < high;
        if (!needX && !needY) return p;

        bool allX = needX && t[p].xmin >= low;
        bool allY = needY && t[p].ymax <= high;
        p = uniqueNode(p);
        if (allX && allY) apply(p, len, low, high, 3);
        else if (allX) apply(p, len, low, 0, 1);
        else if (allY) apply(p, len, 0, high, 2);

        if ((!needX || allX) && (!needY || allY)) return p;
        push(p, len);
        int leftLen = (len + 1) / 2;
        int lc = clip(t[p].lc, leftLen, low, high);
        int rc = clip(t[p].rc, len - leftLen, low, high);
        t[p].lc = lc; t[p].rc = rc;
        pull(p);
        return p;
    }

    // Read-only: carry the unpushed affine tags; never clone or push here.
    u64 query(int p, int len, int low, int high,
              int dx, int dy, unsigned mask) const {
        const Node &v = t[p];
        int xmin = (mask & 1) ? dx : v.xmin + dx;
        int xmax = (mask & 1) ? dx : v.xmax + dx;
        int ymin = (mask & 2) ? dy : v.ymin + dy;
        int ymax = (mask & 2) ? dy : v.ymax + dy;
        bool splitX = false, splitY = false;
        if (xmax > low) {
            if (xmin >= low) { dx = low; mask |= 1; }
            else splitX = true;
        }
        if (ymin < high) {
            if (ymax <= high) { dy = high; mask |= 2; }
            else splitY = true;
        }
        if (!splitX && !splitY) return eval(v, len, dx, dy, mask);

        if (!(mask & 1)) dx += v.tx;
        if (!(mask & 2)) dy += v.ty;
        mask |= v.mask;
        int leftLen = (len + 1) / 2;
        return query(v.lc, leftLen, low, high, dx, dy, mask)
             + query(v.rc, len - leftLen, low, high, dx, dy, mask);
    }

public:
    explicit ProfileTree(int n) {
        t.reserve(size_t(n) * 12 + 64);
        t.push_back(Node{});  // Null node.
    }

    int retain(int p) {
        if (p) ++t[p].refs;
        return p;
    }

    void release(int p) {
        if (!p || --t[p].refs) return;
        int lc = t[p].lc, rc = t[p].rc;
        release(lc);
        release(rc);
        t[p].lc = freeHead;
        freeHead = p;
    }

    int leaf(int value) {
        Node v{};
        v.sx = v.sy = u64(value);
        v.sxy = u64(value) * u64(value);
        v.xmin = v.xmax = v.ymin = v.ymax = value;
        v.refs = 1;
        return alloc(v);
    }

    int merge(int leftRoot, int rightRoot, int len) {
        int low = t[leftRoot].xmin, high = t[leftRoot].ymax;
        int lc = retain(leftRoot);
        int rc = retain(rightRoot);
        rc = clip(rc, len / 2, low, high);
        Node v{};
        v.lc = lc; v.rc = rc; v.refs = 1;
        int p = alloc(v);
        pull(p);
        return p;
    }

    int change(int p, int len, int type, int value) {
        p = uniqueNode(p);
        apply(p, len, value, value, type == 2 ? 3 : 0);
        return p;
    }

    int minimum(int p) const { return t[p].xmin; }
    int maximum(int p) const { return t[p].ymax; }

    u64 contribution(int p, int len, int low, int high, int shift) const {
        return query(p, len, low, high, shift, shift, 0);
    }
#ifdef LOCAL
    size_t allocated() const { return t.size() - 1; }
#endif
};

class Solver {
    int n;
    ProfileTree profiles;
    vector<int> root, add, cover;

    void build(int p, int l, int r, const vector<int> &a) {
        if (l == r) { root[p] = profiles.leaf(a[l]); return; }
        int m = (l + r) / 2;
        build(p * 2, l, m, a);
        build(p * 2 + 1, m + 1, r, a);
        root[p] = profiles.merge(root[p * 2], root[p * 2 + 1], r - l + 1);
    }

    void apply(int p, int len, int type, int value) {
        root[p] = profiles.change(root[p], len, type, value);
        if (type == 2) { cover[p] = value; add[p] = 0; }
        else if (cover[p] != -1) cover[p] += value;
        else add[p] += value;
    }

    void push(int p, int len) {
        int leftLen = (len + 1) / 2, rightLen = len / 2;
        if (cover[p] != -1) {
            apply(p * 2, leftLen, 2, cover[p]);
            apply(p * 2 + 1, rightLen, 2, cover[p]);
            cover[p] = -1;
        }
        if (add[p]) {
            apply(p * 2, leftLen, 1, add[p]);
            apply(p * 2 + 1, rightLen, 1, add[p]);
            add[p] = 0;
        }
    }

    void update(int p, int l, int r, int ql, int qr, int type, int value) {
        if (ql <= l && r <= qr) {
            apply(p, r - l + 1, type, value);
            return;
        }
        // Important: invalidate this profile BEFORE changing its children.
        profiles.release(root[p]);
        root[p] = 0;
        push(p, r - l + 1);
        int m = (l + r) / 2;
        if (ql <= m) update(p * 2, l, m, ql, qr, type, value);
        if (qr > m) update(p * 2 + 1, m + 1, r, ql, qr, type, value);
        root[p] = profiles.merge(root[p * 2], root[p * 2 + 1], r - l + 1);
    }

    u64 query(int p, int l, int r, int ql, int qr,
              int shift, int &low, int &high) const {
        if (cover[p] != -1) {
            int value = cover[p] + shift;
            low = min(low, value); high = max(high, value);
            int len = min(r, qr) - max(l, ql) + 1;
            return u64(low) * u64(high) * u64(len);
        }
        if (ql <= l && r <= qr) {
            u64 ans = profiles.contribution(root[p], r - l + 1, low, high, shift);
            low = min(low, profiles.minimum(root[p]) + shift);
            high = max(high, profiles.maximum(root[p]) + shift);
            return ans;
        }
        shift += add[p];
        int m = (l + r) / 2;
        u64 ans = 0;
        if (ql <= m) ans += query(p * 2, l, m, ql, qr, shift, low, high);
        if (qr > m) ans += query(p * 2 + 1, m + 1, r, ql, qr, shift, low, high);
        return ans;
    }

public:
    explicit Solver(const vector<int> &a)
        : n(int(a.size()) - 1), profiles(n), root(n * 4 + 8),
          add(n * 4 + 8), cover(n * 4 + 8, -1) {
        build(1, 1, n, a);
    }
    void update(int l, int r, int type, int value) {
        update(1, 1, n, l, r, type, value);
    }
    u64 query(int l, int r) const {
        int low = 1000000001, high = 0;
        return query(1, 1, n, l, r, 0, low, high);
    }
#ifdef LOCAL
    size_t allocated() const { return profiles.allocated(); }
#endif
};

int main() {
    static FastIO io;
    int n, q;
    if (!io.readInt(n) || !io.readInt(q)) return 0;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) io.readInt(a[i]);
    Solver solver(a);
    while (q--) {
        int type, l, r, value;
        io.readInt(type); io.readInt(l); io.readInt(r);
        if (type == 3) io.write(solver.query(l, r));
        else {
            io.readInt(value);
            solver.update(l, r, type, value);
        }
    }
#ifdef LOCAL
    fprintf(stderr, "Pool slots: %zu\n", solver.allocated());
#endif
    return 0;
}

Comments

No comments yet.