|
|
#include <iostream>
|
|
|
#include <cstring>
|
|
|
using namespace std;
|
|
|
typedef long long LL;
|
|
|
|
|
|
const int N = 500010;
|
|
|
const int INF = 0x3f3f3f3f;
|
|
|
|
|
|
#define ls u << 1
|
|
|
#define rs ls | 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 { // am为当前最大值,bm为历史最大值,se为严格次大值,cnt为区间内最大值的个数
|
|
|
int l, r; //区间范围
|
|
|
int ma, mb; //当前区间最大值,历史区间最大值
|
|
|
int cnt, se; //最大值个数,次大值
|
|
|
LL sum; //区间和
|
|
|
|
|
|
int add1; //当前区间最大值的加标记
|
|
|
int add2; //当前区间非最大值的加标记
|
|
|
int add3; //当前区间最大值历史上加的最多的那次的加标记
|
|
|
int add4; //当前区间非最大值的历史上加的最多的那次的加标记
|
|
|
} tr[N << 2];
|
|
|
|
|
|
//向上更新统计信息
|
|
|
void pushup(int u) {
|
|
|
//目标:更新u管辖范围内区间和,当前区间最大值,历史区间最大值
|
|
|
tr[u].sum = tr[ls].sum + tr[rs].sum;
|
|
|
tr[u].ma = max(tr[ls].ma, tr[rs].ma);
|
|
|
tr[u].mb = max(tr[ls].mb, tr[rs].mb);
|
|
|
|
|
|
//目标:更新u管辖范围内次大值,最大值个数
|
|
|
//根据左右儿子的最大值分类讨论,共三种情况:
|
|
|
if (tr[ls].ma == tr[rs].ma) { //左区间最大值与右区间最大值相等
|
|
|
tr[u].se = max(tr[ls].se, tr[rs].se); //父区间次大值等于两个子区间次大值中大的一个
|
|
|
tr[u].cnt = tr[ls].cnt + tr[rs].cnt; //父区间最大值个数等于两个子区间的和
|
|
|
} //左区间最大值大于右区间最大值
|
|
|
if (tr[ls].ma > tr[rs].ma) { //不解释了
|
|
|
tr[u].se = max(tr[ls].se, tr[rs].ma);
|
|
|
tr[u].cnt = tr[ls].cnt;
|
|
|
}
|
|
|
if (tr[ls].ma < tr[rs].ma) {
|
|
|
tr[u].se = max(tr[ls].ma, tr[rs].se);
|
|
|
tr[u].cnt = tr[rs].cnt;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//构建线段树
|
|
|
void build(int u, int l, int r) {
|
|
|
tr[u].l = l, tr[u].r = r;
|
|
|
if (l == r) {
|
|
|
tr[u].sum = tr[u].ma = tr[u].mb = read(); //区间和=当前区间最大值=历史区间最大值=节点值
|
|
|
tr[u].se = -INF; //次大值(只有一个节点没有次大值,设置负无穷)
|
|
|
tr[u].cnt = 1; //区间最大值数量=1
|
|
|
return;
|
|
|
}
|
|
|
int mid = (l + r) >> 1;
|
|
|
build(ls, l, mid), build(rs, mid + 1, r);
|
|
|
pushup(u); //向上更新统计信息
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
|
|
*/
|
|
|
|
|
|
inline void update(int u, int k1, int k2, int k3, int k4) {
|
|
|
//区间和的变化
|
|
|
tr[u].sum += ((LL)k1 * tr[u].cnt) + ((LL)k3 * (tr[u].r - tr[u].l + 1 - tr[u].cnt));
|
|
|
|
|
|
//更新历史最大值
|
|
|
tr[u].mb = max(tr[u].mb, tr[u].ma + k2); //什么意思?
|
|
|
|
|
|
// a数组中,最大值被加了个k1
|
|
|
tr[u].ma += k1;
|
|
|
|
|
|
//区间次大值:如果区间不同值个数不为1,那么加上k3
|
|
|
if (tr[u].se != -INF) tr[u].se += k3;
|
|
|
|
|
|
//更新懒标记
|
|
|
//(1) 2和4 需要把取一下max再考虑下传
|
|
|
tr[u].add3 = max(tr[u].add3, tr[u].add1 + k2); //这两句话怎么理解?
|
|
|
tr[u].add4 = max(tr[u].add4, tr[u].add2 + k4); //这两句话怎么理解?
|
|
|
|
|
|
//(2) 1和3直接下传
|
|
|
tr[u].add1 += k1, tr[u].add2 += k3;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
懒标记:
|
|
|
线段树可以支持O(logn)的时间复杂度的区间查询,但如果要进行区间修改的话会导致所有区间的在该区间内的节点的子树下
|
|
|
的子节点的信息都要被修改,时间复杂度度为O(N).
|
|
|
如果之后却根本没有用到修改后的区间的信息则前面的修改操作都是浪费时间的,所以这里我们要给当前节点的区间都在修改
|
|
|
区间内的节点都加上一个懒标记,并完成当前节点的修改,而至于该节点下的子树的修改则是留到下一次查询或者区间修改的
|
|
|
时候将懒标记下传。
|
|
|
|
|
|
回到本题:
|
|
|
(1)pushdown要传递什么?
|
|
|
答:要将add1,add2,add3,add4四个懒标记传递给左右儿子
|
|
|
当前版本最大值加标记,当前版本中非最大值加标记
|
|
|
历史版本最大值加标记,历史版本中非最大值加标记
|
|
|
pushdown只是把本层的统计信息进行更新,并且把懒标记传送给左右儿子节点
|
|
|
|
|
|
(2)pushdown怎么传递?
|
|
|
对于pushdown,我们先判断最大值在哪边,如果在左边,那么左边的最大值受到的影响是当前区间对最大值的影响,
|
|
|
而右边的最大值受到的影响仅是当前区间对非最大值的影响(当然可能左右两边都有最大值,那么两边的最大值受到的
|
|
|
影响均是当前区间对最大值的影响)。如果最大值在右边,反过来就可以了。
|
|
|
*/
|
|
|
void pushdown(int u) {
|
|
|
int mx = max(tr[ls].ma, tr[rs].ma);
|
|
|
|
|
|
if (tr[ls].ma == mx)
|
|
|
update(ls, tr[u].add1, tr[u].add3, tr[u].add2, tr[u].add4);
|
|
|
else
|
|
|
update(ls, tr[u].add2, tr[u].add4, tr[u].add2, tr[u].add4);
|
|
|
|
|
|
if (tr[rs].ma == mx)
|
|
|
update(rs, tr[u].add1, tr[u].add3, tr[u].add2, tr[u].add4);
|
|
|
else
|
|
|
update(rs, tr[u].add2, tr[u].add4, tr[u].add2, tr[u].add4);
|
|
|
|
|
|
//更新后懒标记清零
|
|
|
tr[u].add1 = tr[u].add3 = tr[u].add2 = tr[u].add4 = 0;
|
|
|
}
|
|
|
|
|
|
void change_add(int u, int l, int r, int x) {
|
|
|
if (tr[u].l >= l && tr[u].r <= r) {
|
|
|
update(u, x, x, x, x); //区间内每个位置都要加上x
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
pushdown(u);
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
if (l <= mid) change_add(ls, l, r, x);
|
|
|
if (r > mid) change_add(rs, l, r, x);
|
|
|
|
|
|
pushup(u);
|
|
|
}
|
|
|
|
|
|
void change_min(int u, int l, int r, int x) {
|
|
|
if (tr[u].ma <= x) return; //最大值都比x小,不需要进入
|
|
|
|
|
|
if (tr[u].l >= l && tr[u].r <= r && tr[u].se < x) { // 最大值>x 并且 次大值<x,修改最大值
|
|
|
update(u, x - tr[u].ma, x - tr[u].ma, 0, 0);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
pushdown(u);
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
if (l <= mid) change_min(ls, l, r, x);
|
|
|
if (r > mid) change_min(rs, l, r, x);
|
|
|
pushup(u);
|
|
|
}
|
|
|
|
|
|
LL querysum(int u, int l, int r) { //查询区间和
|
|
|
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
|
|
|
|
|
|
pushdown(u);
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
LL res = 0;
|
|
|
if (l <= mid) res = querysum(ls, l, r);
|
|
|
if (r > mid) res += querysum(rs, l, r);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
int query_ma(int u, int l, int r) { //查询max_a[i]
|
|
|
if (tr[u].l >= l && tr[u].r <= r) return tr[u].ma;
|
|
|
|
|
|
pushdown(u);
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
int res = -INF;
|
|
|
if (l <= mid) res = max(res, query_ma(ls, l, r));
|
|
|
if (r > mid) res = max(res, query_ma(rs, l, r));
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
//复读机
|
|
|
int query_mb(int u, int l, int r) { //查询max_b[i]
|
|
|
if (tr[u].l >= l && tr[u].r <= r) return tr[u].mb;
|
|
|
|
|
|
pushdown(u);
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
int res = -INF;
|
|
|
if (l <= mid) res = max(res, query_mb(ls, l, r));
|
|
|
if (r > mid) res = max(res, query_mb(rs, l, r));
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
//文件输入输出
|
|
|
#ifndef ONLINE_JUDGE
|
|
|
freopen("P6242.in", "r", stdin);
|
|
|
#endif
|
|
|
n = read(), m = read();
|
|
|
|
|
|
build(1, 1, n);
|
|
|
|
|
|
while (m--) {
|
|
|
int op = read(), l = read(), r = read();
|
|
|
if (op == 1) {
|
|
|
int x = read();
|
|
|
change_add(1, l, r, x); //[l,r] -> +x
|
|
|
}
|
|
|
if (op == 2) {
|
|
|
int x = read();
|
|
|
change_min(1, l, r, x); // a[i]=min(a[i],x)
|
|
|
}
|
|
|
if (op == 3) printf("%lld\n", querysum(1, l, r)); //区间和
|
|
|
if (op == 4) printf("%d\n", query_ma(1, l, r)); //当前数组区间最大值
|
|
|
if (op == 5) printf("%d\n", query_mb(1, l, r)); //历史数组区间最大值
|
|
|
}
|
|
|
return 0;
|
|
|
} |