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.

252 lines
9.4 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;
//快读
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;
}
#define ls u << 1
#define rs u << 1 | 1
const int N = 500010;
const int INF = 0x3f3f3f3f;
int n, m;
struct Node {
int l, r;
int mx, cx; //最大值,最大值个数
int sx; //次大值
int mi, ci; //最小值,最小值个数
int si; //次小值
LL sum; //区间和
LL tag; // lazy tag,是不是有未消费掉的add修改标记
} 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);
}
}
//构建线段树
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);
}
//将区间内所有数字更新为 max(a[i],v)
bool update_max(int u, int v) {
//① 区间最小值都大于v,不需要进入区间
if (tr[u].mi >= v) return true;
//② 如果区间内数字都是一样的
if (tr[u].mx == tr[u].mi) {
tr[u].mx = tr[u].mi = v;
tr[u].cx = tr[u].ci = tr[u].r - tr[u].l + 1;
tr[u].sx = -INF, tr[u].si = INF;
tr[u].sum = (LL)tr[u].cx * v;
return true;
}
//③ 如果区间次小值大于v,只能更新最小值
if (tr[u].si > v) {
tr[u].sum += (LL)tr[u].ci * (v - tr[u].mi);
tr[u].mi = v;
tr[u].sx = max(tr[u].sx, v);
tr[u].mx = max(tr[u].mx, v);
return true;
}
//④ 以上情况都不是,那么讨论不清楚,只能是继续递归左右儿子,在子区间内尝试上面的三种情况更新
return false;
}
//将区间内所有数字更新为 min(a[i],v)
bool update_min(int u, int v) {
//① 区间最大值都小于v,不需要进入区间
if (tr[u].mx <= v) return true;
//② 如果区间内数字都是一样的
if (tr[u].mx == tr[u].mi) {
tr[u].mx = tr[u].mi = v;
tr[u].cx = tr[u].ci = tr[u].r - tr[u].l + 1;
tr[u].sx = -INF, tr[u].si = INF;
tr[u].sum = (LL)tr[u].cx * v;
return true;
}
//③ 如果区间次大值小于v,只能更新最大值
if (tr[u].sx < v) {
tr[u].sum -= (LL)tr[u].cx * (tr[u].mx - v);
tr[u].mx = v;
tr[u].si = min(tr[u].si, v);
tr[u].mi = min(tr[u].mi, v);
return true;
}
//④ 以上情况都不是,那么讨论不清楚,只能是继续递归左右儿子,在子区间内尝试上面的三种情况更新
return false;
}
//整体命中时区间每个数都要加上v,修改u节点的统计信息并且传递tag懒标记
void update_add(int u, int v) {
//① 最大值最小值次大值次小值都会受到影响都要加上v
tr[u].mx += v, tr[u].mi += v, tr[u].sx += v, tr[u].si += v;
//② 区间和也需要进行整体更新
tr[u].sum += (LL)v * (tr[u].r - tr[u].l + 1); //为防止乘法受成的int溢出需要先强制转换为LL
//③ 区间加的tag懒标记需要标识在此区间上并没有真的一路更新到叶子节点
tr[u].tag += v;
}
//向下传递懒标记,一般是有新的更新操作,或者,有了查询操作时才这样做
void pushdown(int u) {
if (tr[u].tag) { //如果区间加懒标记不为0表示存在懒标记需要通过update_add传递给左右儿子
//主要的目标有两个1、根据懒标记实时更新左右儿子的统计信息2、左右儿子继承写上父亲的区间加懒标记
update_add(ls, tr[u].tag), update_add(rs, tr[u].tag);
//传递完毕,父节点区间加懒标记清零
tr[u].tag = 0;
}
//其实本题的懒标记不只是区间加的tag,还有最大值tag和最小值tag,只不过没有显示的写出来
//① 如果父亲的最大值标签信息比左儿子的最大值小这就是有问题需要用tr[u].mx 对左儿子取min操作
if (tr[ls].mx > tr[u].mx) update_min(ls, tr[u].mx);
//② 如果父亲的最大值标签信息比右儿子的最大值小这就是有问题需要用tr[u].mx 对右儿子取min操作
if (tr[rs].mx > tr[u].mx) update_min(rs, tr[u].mx);
//③ 如果父亲的最小值标签信息比左儿子的最小值大这就是有问题需要用tr[u].mi 对左儿子取max操作
if (tr[ls].mi < tr[u].mi) update_max(ls, tr[u].mi);
//④ 如果父亲的最小值标签信息比右儿子的最小值大这就是有问题需要用tr[u].mi 对右儿子取max操作
if (tr[rs].mi < tr[u].mi) update_max(rs, tr[u].mi);
}
//区间加
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_add(u, v); //更新统计信息+修改tag懒标记
return;
}
//① 未完全命中存在部分交集先将以前的tag传递给左右儿子再开始递归左右儿子
pushdown(u);
//② 分裂处理左右儿子
modify_add(ls, l, r, v), modify_add(rs, l, r, v);
//③ 因为子节点信息更新了,需要向上汇集统计信息
pushup(u);
}
//更新最大值操作拆分成了modify_max+update_max两个函数
void modify_max(int u, int l, int r, int v) {
if (tr[u].l > r || tr[u].r < l) return; //无交集,直接返回
if (l <= tr[u].l && tr[u].r <= r) //如果区间完整命中
if (update_max(u, v)) return; //需要分类讨论只有3种情况是可以进行更新的其它情况只能继续分裂
//分裂
pushdown(u);
modify_max(ls, l, r, v), modify_max(rs, l, r, v);
pushup(u);
}
//更新最小值操作拆分成了modify_min+update_min两个函数
void modify_min(int u, int l, int r, int v) {
if (l > tr[u].r || r < tr[u].l) return;
if (l <= tr[u].l && tr[u].r <= r)
if (update_min(u, v)) return;
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 (tr[u].l >= 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 (tr[u].l >= 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 (tr[u].l >= 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;
}