- [l,r] 区间,把大于x的变成x
- 求区间最大值
- 求区间和
普通线段树不能做到区间取min操作,但是吉老师提出了一个方法传送门
在普通线段树基础上,每个结点维护的值有sum表示区间和,mx表示区间最大值,cnt表示区间最大值出现的次数,se表示区间次大值
其核心想法在于
- 当 \(m a \leq x\) 时,显然这一次修改不会对这个节点产生影响,直接退出。
- 当se \(<x<m a\) 时,显然这一次修改只会影响到所有最大值,所以我们 把num加上cnt \(\cdot(x-m a)\), 把 \(m a\) 更新为 \(x\) ,接看打上标记然后退出。
- 当se \(\geq x\) 时,我们无法直接更新这一个节点的信息,因此在这时,我们对
当前节点的左儿子与右儿子进行递归搜索。
特点在于没有去用lazy标记,而是直接利用左右孩子区间的最大值和整个区间的最大值比较来判断整整个区间是否要进行变化。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6 + 5;
int a[N];
struct Segment_Tree{
struct Node {
int l, r, mx, se, cnt; // 区间最大值mx, 区间次大值se, 区间最大值个数cnt
ll sum;
#define l(p) tree[p].l
#define r(p) tree[p].r
#define mx(p) tree[p].mx
#define se(p) tree[p].se
#define cnt(p) tree[p].cnt
#define sum(p) tree[p].sum
#define lson (p << 1)
#define rson (p << 1 | 1)
} tree[N << 2];
void pushup(int p) {
sum(p) = sum(lson) + sum(rson); // 所有sum都是这样的
mx(p) = max(mx(lson), mx(rson)); // 所有max都是这样
cnt(p) = 0;
if(mx(lson) == mx(rson)) {
cnt(p) = cnt(lson) + cnt(rson);
se(p) = max(se(lson), se(rson));
}else {
if(mx(lson) > mx(rson)) {
cnt(p) = cnt(lson);
se(p) = max(se(lson), mx(rson));
} else {
cnt(p) = cnt(rson);
se(p) = max(se(rson), mx(lson));
}
}
}
void pushdown(int p) {
if(mx(lson) > mx(p)) {
sum(lson) -= 1ll * (mx(lson) - mx(p)) * cnt(lson);
mx(lson) = mx(p);
}
if(mx(rson) > mx(p)) {
sum(rson) -= 1ll * (mx(rson) - mx(p)) * cnt(rson);
mx(rson) = mx(p);
}
}
void build(int p, int l, int r){
l(p) = l, r(p) = r, mx(p) = cnt(p) = sum(p) = 0;
se(p) = -1;
if(l == r) {
sum(p) = mx(p) = a[l];
se(p) = -1; cnt(p) = 1;
return ;
}
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid + 1, r);
pushup(p);
}
void Change_min(int p, int l, int r, int x){ // [l,r] a[i] = min(a[i], x);
if(mx(p) <= x) return;
if(l <= l(p) && r(p) <= r && se(p) < x) {
sum(p) -= 1ll * (mx(p) - x) * cnt(p);
mx(p) = x;
return;
}
pushdown(p);
int mid = (l(p) + r(p)) >> 1;
if(l <= mid) Change_min(lson, l, r, x);
if(r > mid) Change_min(rson, l, r, x);
pushup(p);
}
int Query_max(int p, int l, int r) {
if(l <= l(p) && r(p) <= r) return mx(p);
pushdown(p);
int mid = (l(p) + r(p)) >> 1;
int ans = 0;
if(l <= mid) ans = max(ans, Query_max(lson, l, r));
if(r > mid) ans = max(ans, Query_max(rson, l, r));
return ans;
}
ll Query_sum(int p, int l, int r) {
if(l <= l(p) && r(p) <= r) return sum(p);
pushdown(p);
int mid = (l(p) + r(p)) >> 1;
ll ans = 0;
if(l <= mid) ans += Query_sum(lson, l, r);
if(r > mid) ans += Query_sum(rson, l, r);
return ans;
}
} seg;
void solve(){
int n, m; scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
seg.build(1, 1, n);
for(int i = 1; i <= m; i++) {
int op, l, r, x;
scanf("%d%d%d", &op, &l, &r);
if(op == 0) {
scanf("%d", &x); seg.Change_min(1, l, r, x);
}
if(op == 1) printf("%d\n", seg.Query_max(1, l, r));
if(op == 2) printf("%lld\n", seg.Query_sum(1, l, r));
}
}
int main(){
int t; scanf("%d", &t);
for(int i = 1; i <= t; i++) solve();
return 0;
}