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

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