Segment Tree Beats 学习笔记
此类线段树一般用来处理区间最值操作或者区间历史最值问题。
例如 线段树3,支持如下操作:
1 l r k
:将 \(a_l\sim a_r\) 加上 \(k\)。
2 l r v
:将 \(a_l\sim a_r\) 对 \(v\) 取最小值。
3 l r
:求 \(a_l\sim a_r\) 的和。
4 l r
:求 \(a_l\sim a_r\) 的最大值。
5 l r
:求 \(b_l\sim b_r\) 的最大值。(\(b_i\) 即位置 \(i\) 上的历史最大值)
对于操作2,区间取 \(\min\),意味着只对那些大于 \(v\) 的数有更改。因此这个操作的对象不再是整个区间,而是「这个区间中大于 \(v\) 的数」。于是我们可以有这样的思路:每个结点维护该区间的最大值 \(mx\)、次大值 \(se\)、区间和 \(sum\) 以及最大值的个数 \(cnt\)。接下来我们考虑区间对 \(v\) 取 \(\min\) 的操作。
-
如果 \(mx\le v\),显然这个 \(v\) 是没有意义的,直接返回;
-
如果 \(se<v\le mx\),那么这个 \(v\) 就能更新当前区间中的最大值。于是我们让区间和加上 \(cnt(v-mx)\),然后更新 \(mx\) 为 \(v\),并打一个标记。
-
如果 \(v\le se\),那么这时你发现你不知道有多少个数涉及到更新的问题。于是我们的策略就是,暴力递归向下操作。然后上传信息。
复杂度由创始人吉老师的分析大约是 \(\mathcal O(q\log n)\) 的。
对于操作1,3,4。我们需要下传标记处理。我们还需要维护区间和,区间 \(\max\) 与区间加的标记。
再考虑操作5,维护区间历史最值。
假设只有区间加的操作,我们维护标记 \(Add\) 表示当前区间增加的值,这个标记可以解决区间 \(\max\) 的问题。接下来考虑历史区间 \(\max\)。我们定义标记 \(Pre\),该标记的含义是:在该标记的生存周期内,\(Add\) 标记的历史最大值。
我们先对一个标记的生存周期下定义。
一个标记会经历这样的过程:
- 在结点 \(u\) 被建立。
- 在结点 \(u\) 接受若干个新的标记的同时,与新的标记合并(指同类标记)。
- 结点 \(u\) 的标记下传给 \(u\) 的儿子,\(u\) 的标记清空。
我们认为在这个过程中,从 \(1\) 开始到 \(3\) 之前,都是结点 \(u\) 的标记的生存周期。两个标记合并后,成为同一个标记,那么他们的生存周期也会合并(即取建立时间较早的那个做为生存周期的开始)。
利用这个概念,我们可以证明:在一个结点标记的生存周期内,其子结点均不会发生任何变化,并保留在这个生存周期之前的状态。道理很简单,因为在这个期间你是没有下传标记的。
于是,你就可以保证,在当前标记生存周期内的历史 \(Add\) 的最大值是可以更新到子结点的标记和信息上的。因为子结点的标记和信息在这个时间段内都没有变过。于是我们把 \(u\) 的标记下传给它的儿子 \(s\),不难发现有:
那么信息的更新也是类似的,拿对应的标记更新即可。
又由于区间取 \(\min\) 操作只会更新区间最大值。我们就需要把最大值和非最大值分开来维护,需要多设两个 \(tag\)。否则就更新不了非最大历史最大值的历史最大值了。
我们把一个区间的元素划分成最大值和非最大值两部分,那么区间最值操作可以转化为对最大值的加减操作,而题目中的区间加减则同时作用于这两类值。于是我们以一个 \(\log\) 的代价把问题就变成了两个数域上的区间加减操作和历史最值询问。
总体复杂度为 \(\mathcal O(q\log^2n)\)。
总结一下,在这道题,我们在线段树的每一个节点上需要存储的信息有:区间总和 $ sum$,区间最大值 \(mx\),区间历史最大值 \(mx'\),区间严格次大值 \(se\),区间最大值的个数 \(cnt\),区间最大值的懒标记 \(tag_1\),区间最大值的懒标记的最大值 \(tag_1'\),区间非最大值的懒标记 \(tag_2\),区间非最大值的懒标记的最大值 \(tag_2'\)。
code:
#include<bits/stdc++.h>
#define INF 0x7fffffff
constexpr int N(5e5);
using ll=long long;
int n,m;
int a[N+5],b[N+5];
namespace SMT{
struct node{
ll sum;
int mx,se,cnt,mx_;
int tagmx,tagmx_,tagnmx,tagnmx_;
inline void cleartag(){tagmx=tagmx_=tagnmx=tagnmx_=0;}
}T[N+1<<2];
inline void pushup(int o){
T[o].sum=T[o<<1].sum+T[o<<1|1].sum;
T[o].mx_=std::max(T[o<<1].mx_,T[o<<1|1].mx_);
if(T[o<<1].mx==T[o<<1|1].mx){
T[o].mx=T[o<<1].mx;
T[o].se=std::max(T[o<<1].se,T[o<<1|1].se);
T[o].cnt=T[o<<1].cnt+T[o<<1|1].cnt;
}
else if(T[o<<1].mx>T[o<<1|1].mx){
T[o].mx=T[o<<1].mx;
T[o].se=std::max(T[o<<1].se,T[o<<1|1].mx);
T[o].cnt=T[o<<1].cnt;
}
else{
T[o].mx=T[o<<1|1].mx;
T[o].se=std::max(T[o<<1].mx,T[o<<1|1].se);
T[o].cnt=T[o<<1|1].cnt;
}
}
inline void update(int o,int l,int r,int k1,int k1_,int k2,int k2_){
T[o].sum+=1ll*k1*T[o].cnt+1ll*k2*(r-l+1-T[o].cnt);
T[o].mx_=std::max(T[o].mx_,T[o].mx+k1_);T[o].mx+=k1;
T[o].tagmx_=std::max(T[o].tagmx_,T[o].tagmx+k1_);
T[o].tagnmx_=std::max(T[o].tagnmx_,T[o].tagnmx+k2_);
if(T[o].se!=-INF)T[o].se+=k2;
T[o].tagmx+=k1,T[o].tagnmx+=k2;
}
inline void pushdown(int o,int l,int r,int mid){
int tmp=std::max(T[o<<1].mx,T[o<<1|1].mx);
if(T[o<<1].mx==tmp)update(o<<1,l,mid,T[o].tagmx,T[o].tagmx_,T[o].tagnmx,T[o].tagnmx_);
else update(o<<1,l,mid,T[o].tagnmx,T[o].tagnmx_,T[o].tagnmx,T[o].tagnmx_);
if(T[o<<1|1].mx==tmp)update(o<<1|1,mid+1,r,T[o].tagmx,T[o].tagmx_,T[o].tagnmx,T[o].tagnmx_);
else update(o<<1|1,mid+1,r,T[o].tagnmx,T[o].tagnmx_,T[o].tagnmx,T[o].tagnmx_);
T[o].cleartag();
}
void modify1(int o,int l,int r,int ql,int qr,int v){
if(qr<l||ql>r)return;
if(ql<=l&&r<=qr)return update(o,l,r,v,v,v,v);
int mid=l+r>>1;pushdown(o,l,r,mid);
modify1(o<<1,l,mid,ql,qr,v);
modify1(o<<1|1,mid+1,r,ql,qr,v);
pushup(o);
}
void modify2(int o,int l,int r,int ql,int qr,int v){
if(qr<l||ql>r||v>=T[o].mx)return;
if(ql<=l&&r<=qr&&v>T[o].se)return update(o,l,r,v-T[o].mx,v-T[o].mx,0,0);
int mid=l+r>>1;pushdown(o,l,r,mid);
modify2(o<<1,l,mid,ql,qr,v);
modify2(o<<1|1,mid+1,r,ql,qr,v);
pushup(o);
}
ll query3(int o,int l,int r,int ql,int qr){
if(qr<l||ql>r)return 0;
if(ql<=l&&r<=qr)return T[o].sum;
int mid=l+r>>1;pushdown(o,l,r,mid);
return query3(o<<1,l,mid,ql,qr)+query3(o<<1|1,mid+1,r,ql,qr);
}
ll query4(int o,int l,int r,int ql,int qr){
if(qr<l||ql>r)return -INF;
if(ql<=l&&r<=qr)return T[o].mx;
int mid=l+r>>1;pushdown(o,l,r,mid);
return std::max(query4(o<<1,l,mid,ql,qr),query4(o<<1|1,mid+1,r,ql,qr));
}
ll query5(int o,int l,int r,int ql,int qr){
if(qr<l||ql>r)return -INF;
if(ql<=l&&r<=qr)return T[o].mx_;
int mid=l+r>>1;pushdown(o,l,r,mid);
return std::max(query5(o<<1,l,mid,ql,qr),query5(o<<1|1,mid+1,r,ql,qr));
}
void build(int o,int l,int r){
if(l==r){
T[o].sum=T[o].mx=T[o].mx_=a[l];
T[o].se=-INF;T[o].cnt=1;
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
}
int main(){
std::cin.tie(nullptr)->sync_with_stdio(false);
std::cin>>n>>m;
for(int i=1;i<=n;++i)std::cin>>a[i];
SMT::build(1,1,n);
while(m--){
static int opt,l,r,x;
std::cin>>opt>>l>>r;
switch(opt){
case 1:std::cin>>x;SMT::modify1(1,1,n,l,r,x);break;
case 2:std::cin>>x;SMT::modify2(1,1,n,l,r,x);break;
case 3:std::cout<<SMT::query3(1,1,n,l,r)<<'\n';break;
case 4:std::cout<<SMT::query4(1,1,n,l,r)<<'\n';break;
case 5:std::cout<<SMT::query5(1,1,n,l,r)<<'\n';break;
}
}
return 0;
}