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.

227 lines
7.8 KiB

2 years ago
#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;
}