#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;
} QOJ.ac
QOJ
Discussion #2666 for Problem #14940. Square Kingdom
Type: Editorial
Status: Open
Posted by: Anonymous
Posted at: 2026-09-12 19:11:23
Last updated: 2026-09-12 20:14:08
$O(\sqrt{k})$ by GPT6 Pro
Comments
No comments yet.