传送门
要求支持操作:区间取
m
i
n
min
min,区间求
m
a
x
max
max,区间求和。
取
m
i
n
min
min操作维护方式:记录一个最大值和最大值出现次数和次大值。
对于区间对
x
x
x取
m
i
n
min
min操作,当前递归到
r
o
o
t
root
root节点。用
m
a
x
max
max记录最大值,
s
u
b
sub
sub记录次大值。分类讨论:
①
x
>
=
T
[
r
o
o
t
]
.
m
a
x
x>=T[root].max
x>=T[root].max直接返回。
②
T
[
r
o
o
t
]
.
s
u
b
<
x
<
T
[
r
o
o
t
]
.
m
a
x
T[root].sub<x<T[root].max
T[root].sub<x<T[root].max,现在只会将这些最大值改为
x
x
x,那么
s
u
m
sum
sum减少
T
[
r
o
o
t
]
.
c
n
t
∗
(
T
[
r
o
o
t
]
.
m
a
x
−
x
)
T[root].cnt*(T[root].max-x)
T[root].cnt∗(T[root].max−x)。其中
c
n
t
cnt
cnt为最大值出现次数。
③
x
<
=
T
[
r
o
o
t
]
.
s
u
b
x<=T[root].sub
x<=T[root].sub,那么递归两个儿子继续做。
其实一个节点的
m
a
x
max
max也可以看做是一个对它子树取
m
i
n
min
min的标记。
然后稍微注意
p
u
s
h
u
p
pushup
pushup和
p
u
s
h
d
o
w
n
pushdown
pushdown即可。把叶子节点的次小值赋为
−
1
-1
−1是保证在叶子节点的时候会返回,防止死循环。
复杂度是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n),不知道为什么,可能是由于某股神秘的东方力量。。?
#include<bits/stdc++.h>
#define mid (T[root].l+T[root].r>>1)
#define lc (root<<1)
#define rc (root<<1|1)
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int Case,n,m,op,l,r,x,a[maxn];
struct node{
int l,r,mx,sb,cnt;
ll sum;
}T[maxn<<2];
inline void pushup(int root){
T[root].sum=T[lc].sum+T[rc].sum;
T[root].mx=max(T[lc].mx,T[rc].mx);
T[root].sb=max((T[lc].mx==T[root].mx)?T[lc].sb:T[lc].mx,(T[rc].mx==T[root].mx)?T[rc].sb:T[rc].mx);
T[root].cnt=((T[lc].mx==T[root].mx)?T[lc].cnt:0)+((T[rc].mx==T[root].mx)?T[rc].cnt:0);
}
inline void build(int root,int l,int r){
T[root].l=l,T[root].r=r;
if(l==r){T[root].sum=T[root].mx=a[l],T[root].sb=-1,T[root].cnt=1;return;}
build(lc,l,mid),build(rc,mid+1,r),pushup(root);
}
//val 取min
inline void pushnow(int root,int val){
if(val>=T[root].mx) return;
T[root].sum+=1ll*T[root].cnt*(val-T[root].mx);
T[root].mx=val;
}
inline void pushdown(int root){pushnow(lc,T[root].mx),pushnow(rc,T[root].mx);}
inline void minimize(int root,int l,int r,int val){
if(val>=T[root].mx) return;
if(l<=T[root].l&&T[root].r<=r&&val>T[root].sb)
return pushnow(root,val);
pushdown(root);
if(l<=mid) minimize(lc,l,r,val);
if(r> mid) minimize(rc,l,r,val);
pushup(root);
}
inline ll getmax(int root,int l,int r){
if(l<=T[root].l&&T[root].r<=r) return T[root].mx;
pushdown(root);
if(r<=mid) return getmax(lc,l,r);
if(l> mid) return getmax(rc,l,r);
return max(getmax(lc,l,r),getmax(rc,l,r));
}
inline ll getsum(int root,int l,int r){
if(l<=T[root].l&&T[root].r<=r) return T[root].sum;
pushdown(root);
if(r<=mid) return getsum(lc,l,r);
if(l> mid) return getsum(rc,l,r);
return getsum(lc,l,r)+getsum(rc,l,r);
}
int main(){
scanf("%d",&Case);
while(Case--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",a+i);
build(1,1,n);
while(m--){
scanf("%d%d%d",&op,&l,&r);
if(op==0) scanf("%d",&x),minimize(1,l,r,x);
if(op==1) printf("%lld\n",getmax(1,l,r));
if(op==2) printf("%lld\n",getsum(1,l,r));
}
}
}