You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
9.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}