Files
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

244 lines
9.3 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 550000;
const int INF = 0x3f3f3f3f;
#define ls u << 1
#define rs u << 1 | 1
//快读
int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n, m;
struct Node {
int l, r;
int mx, cx; //最大值,最大值个数
int sx; //次大值
int mi, ci; //最小值,最小值个数
int si; //次小值
LL sum; //区间和
int add1, add2, add3; //最小值增加,最大值增加,普通值增加
} tr[N << 2];
//向上推送统计信息,由左右儿子的统计信息更新父亲u的统计信息
void pushup(int u) {
tr[u].sum = tr[ls].sum + tr[rs].sum; //区间和
//更新最大值系列信息 mx,sx,cx
if (tr[ls].mx == tr[rs].mx) {
tr[u].mx = tr[ls].mx;
tr[u].cx = tr[ls].cx + tr[rs].cx;
tr[u].sx = max(tr[ls].sx, tr[rs].sx);
} else if (tr[ls].mx > tr[rs].mx) {
tr[u].mx = tr[ls].mx;
tr[u].cx = tr[ls].cx;
tr[u].sx = max(tr[ls].sx, tr[rs].mx);
} else if (tr[ls].mx < tr[rs].mx) {
tr[u].mx = tr[rs].mx;
tr[u].cx = tr[rs].cx;
tr[u].sx = max(tr[ls].mx, tr[rs].sx);
}
//更新最小值系列信息 mi,si,ci
if (tr[ls].mi == tr[rs].mi) {
tr[u].mi = tr[ls].mi;
tr[u].si = min(tr[ls].si, tr[rs].si);
tr[u].ci = tr[ls].ci + tr[rs].ci;
} else if (tr[ls].mi > tr[rs].mi) {
tr[u].mi = tr[rs].mi;
tr[u].ci = tr[rs].ci;
tr[u].si = min(tr[ls].mi, tr[rs].si);
} else if (tr[ls].mi < tr[rs].mi) {
tr[u].mi = tr[ls].mi;
tr[u].ci = tr[ls].ci;
tr[u].si = min(tr[ls].si, tr[rs].mi);
}
}
/*
k1:最小值增量,k2:最大值增量,k3:一般值增量
*/
void update(int u, int k1, int k2, int k3) {
/*
① 更新区间和
如果一个区间的值域很小(比如只有1或2个数),可能会发生一个值既是最大值又是次小值这种情况,也就是发生了数域的重叠.
这种情况要特判,分辨到底该被哪个标记作用。
*/
if (tr[u].mi == tr[u].mx) { // 只有一种值时,最大值等于最小值
// if (k1 == k3) k1 = k2;
// if (k2 == k3) k2 = k1;
// 不应被其他值的标记作用
if (k1 == k3)
k1 = k2;
else
k2 = k1;
tr[u].sum += (LL)k1 * tr[u].ci;
} else
//整体增量=最小值增量*个数+最大值增量*个数+普通值增量*个数
tr[u].sum += (LL)k1 * tr[u].ci + (LL)k2 * tr[u].cx + (LL)k3 * (tr[u].r - tr[u].l + 1 - tr[u].ci - tr[u].cx);
//② 更新次小
if (tr[u].si == tr[u].mx) //次小等于最大
tr[u].si += k2;
else if (tr[u].si != INF) //次小存在
tr[u].si += k3;
//③ 更新次大
if (tr[u].sx == tr[u].mi) //次大等于最小
tr[u].sx += k1;
else if (tr[u].sx != -INF) //次大存在
tr[u].sx += k3;
//④ 更新最小值,最大值
tr[u].mi += k1, tr[u].mx += k2;
//⑤ 向左右儿子传递懒标记add1,add2,add3
tr[u].add1 += k1, tr[u].add2 += k2, tr[u].add3 += k3;
}
//向下传递懒标记,一般是有新的更新操作,或者,有了查询操作时才这样做
void pushdown(int u) {
int mi = min(tr[ls].mi, tr[rs].mi);
int mx = max(tr[ls].mx, tr[rs].mx);
/*
k1:最小值增量,k2:最大值增量,k3:一般值增量
有三个lazy add标签,需要注意互相之间的影响关系:
① 修改区间最小值时要注意其对最大值、严格次大值的影响
② 修改区间最大值时要注意其对最小值、严格次小值的影响
*/
int k1, k2, k3 = tr[u].add3; //一般值没啥说道,直接传递给左右儿子就行
tr[ls].mi == mi ? k1 = tr[u].add1 : k1 = tr[u].add3; //左儿子中有最小值,k1=tr[u].add1,否则k1=tr[u].add3
tr[ls].mx == mx ? k2 = tr[u].add2 : k2 = tr[u].add3; //左儿子中有最大值,k2=tr[u].add2,否则k1=tr[u].add3
update(ls, k1, k2, k3); //标签纠正好了,可以更新左儿子了
//复读机
tr[rs].mi == mi ? k1 = tr[u].add1 : k1 = tr[u].add3; //右儿子中有最小值,k1=tr[u].add1,否则k1=tr[u].add3
tr[rs].mx == mx ? k2 = tr[u].add2 : k2 = tr[u].add3; //右儿子中有最大值,k2=tr[u].add2,否则k1=tr[u].add3
update(rs, k1, k2, k3); //标签纠正好了,可以更新右儿子了
//传递完了add lzay tag,重置为0,表示没的传了
tr[u].add1 = tr[u].add2 = tr[u].add3 = 0;
}
//构建线段树
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].mx = tr[u].mi = tr[u].sum = read(); //最大值,最小值,区间和
tr[u].cx = tr[u].ci = 1; //最大值个数=最小值个数=1
tr[u].sx = -INF; //没有次大值
tr[u].si = INF; //没有次小值
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
//区间加
void modify_add(int u, int l, int r, int v) {
if (tr[u].l > r || tr[u].r < l) return; //无交集,直接返回
if (tr[u].l >= l && tr[u].r <= r) { //区间完整命中
update(u, v, v, v); //更新统计信息,最大值,最小值,一般值 都是一样的需要+v
return;
}
//① 未完全命中,存在部分交集,先将以前的tag传递给左右儿子,再开始递归左右儿子
pushdown(u);
//② 分裂处理左右儿子
modify_add(ls, l, r, v), modify_add(rs, l, r, v);
//③ 因为子节点信息更新了,需要向上汇集统计信息
pushup(u);
}
//区间修改最大值
void modify_max(int u, int l, int r, int v) {
if (tr[u].l > r || tr[u].r < l || v <= tr[u].mi) return; //如果区间无交集,或者,要更新的最大值还没有人家区间的最小值大,没有更新的必要
if (l <= tr[u].l && tr[u].r <= r && v < tr[u].si) { //如果区间完全命中,并且,v小于区间次小值,那么只能更新最小值
update(u, v - tr[u].mi, 0, 0);
//最小值被更新的大小=为add(v-tr[u].mi),如此,成功将设置为最小值操作转化为加法标签操作
return;
}
//① 未完全命中,存在部分交集,先将以前的tag传递给左右儿子,再开始递归左右儿子
pushdown(u);
//② 分裂处理左右儿子
modify_max(ls, l, r, v), modify_max(rs, l, r, v);
//③ 因为子节点信息更新了,需要向上汇集统计信息
pushup(u);
}
//区间修改最小值
void modify_min(int u, int l, int r, int v) {
if (tr[u].l > r || tr[u].r < l || v >= tr[u].mx) return; //如果区间无交集,或者,要更新的最小值还比人家区间的最大值还大,没有更新的必要
if (l <= tr[u].l && tr[u].r <= r && v > tr[u].sx) { //如果区间完全命中,并且,v大于区间次大值,那么只能更新最大值
update(u, 0, v - tr[u].mx, 0);
//最大值被更新的大小=为add(v-tr[u].mx),如此,成功将设置为最大值操作转化为加法标签操作
return;
}
//① 未完全命中,存在部分交集,先将以前的tag传递给左右儿子,再开始递归左右儿子
pushdown(u);
//② 分裂处理左右儿子
modify_min(ls, l, r, v), modify_min(rs, l, r, v);
//③ 因为子节点信息更新了,需要向上汇集统计信息
pushup(u);
}
//查询区间和
LL query_sum(int u, int l, int r) {
if (tr[u].l > r || tr[u].r < l) return 0;
if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
return query_sum(ls, l, r) + query_sum(rs, l, r);
}
//区间最大值
int query_max(int u, int l, int r) {
if (tr[u].l > r || tr[u].r < l) return -INF;
if (l <= tr[u].l && tr[u].r <= r) return tr[u].mx;
pushdown(u);
return max(query_max(ls, l, r), query_max(rs, l, r));
}
//区间最小值
int query_min(int u, int l, int r) {
if (tr[u].l > r || tr[u].r < l) return INF;
if (l <= tr[u].l && tr[u].r <= r) return tr[u].mi;
pushdown(u);
return min(query_min(ls, l, r), query_min(rs, l, r));
}
int main() {
//文件输入输出
#ifndef ONLINE_JUDGE
freopen("BZOJ4695.in", "r", stdin);
#endif
n = read();
build(1, 1, n);
m = read();
while (m--) {
int op = read(), l = read(), r = read(), x;
if (op == 1) x = read(), modify_add(1, l, r, x); //区间加
if (op == 2) x = read(), modify_max(1, l, r, x); //区间修改max运算
if (op == 3) x = read(), modify_min(1, l, r, x); //区间修改min运算
if (op == 4) printf("%lld\n", query_sum(1, l, r)); //查询区间和
if (op == 5) printf("%d\n", query_max(1, l, r)); //查询区间最大值
if (op == 6) printf("%d\n", query_min(1, l, r)); //查询区间最小值
}
return 0;
}