吉司机线段树
为了方便说板子,这里直接把板子题放上去讲了。
线段树 3
简单说一下 \(5\) 个操作都在干什么:
-
区间加一个数。
-
区间和一个数取最小值。
-
区间求和。
-
区间求最大值。
-
区间求历史最大值。
好了,前 \(4\) 个操作如果单独拉出来出成一道题,显然是好做的,于是我们的难点就是最后一个操作。
因此,我们的线段树要维护很多东西:区间左右端点,区间和,最大值,严格次大值等,具体注释放在下面代码里了,因为实在太多了:
struct node{
int l,r,sum;
int mx_st,mx_se,mx_hi,mx_num;
int lzy_st,lzy_hi,lzy_se,lzy_he;
/*
mx_st 区间最大,mx_se 区间严格次大,mx_hi 区间历史最大,mx_sum 区间最大出现次数
lzy_st 最大值加减标记,lzy_hi 历史最大值加减标记
lzy_se 非最大值加减标记,lzy_he 非历史最大值加减标记
*/
}tr[N<<2];
这里解释一下区间最大出现次数指的是在一个区间 \([l,r]\) 中有几个数是当前区间的最大值。
接下来我们分模块讲一下各个操作:
建树
其他的部分和正常线段树一样,不过因为叶子节点只有一个值,故严格次大值应该附成极小值。
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
tr[u].sum=tr[u].mx_st=tr[u].mx_hi=a[l];
tr[u].mx_num=1;
tr[u].mx_se=-inf;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
上传
我们需要更新的是区间和,最大值,严格次大值,历史最大值,最大值出现次数。
这里说一下不太好弄的严格次大值:显然如果两个儿子的最大值相同,那么严格次大值就是两边的严格次大值的较大的那个。
否则,我们设儿子 \(a\) 的最大值比儿子 \(b\) 的最大值更大,于是严格次大值就是儿子 \(a\) 的严格次大值和儿子 \(b\) 的最大值的较大值。
其它的更新见代码:
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
tr[u].mx_st=max(tr[u<<1].mx_st,tr[u<<1|1].mx_st);
tr[u].mx_hi=max(tr[u<<1].mx_hi,tr[u<<1|1].mx_hi);
if(tr[u<<1].mx_st==tr[u<<1|1].mx_st){
tr[u].mx_num=tr[u<<1].mx_num+tr[u<<1|1].mx_num;
tr[u].mx_se=max(tr[u<<1].mx_se,tr[u<<1|1].mx_se);
}
else if(tr[u<<1].mx_st>tr[u<<1|1].mx_st){
tr[u].mx_num=tr[u<<1].mx_num;
tr[u].mx_se=max(tr[u<<1].mx_se,tr[u<<1|1].mx_st);
}
else{
tr[u].mx_num=tr[u<<1|1].mx_num;
tr[u].mx_se=max(tr[u<<1].mx_st,tr[u<<1|1].mx_se);
}
}
下传
这里拆成了两个部分 \(modify\) 和 \(pushdown\)。
先说一下 \(modify\):
我们传进去的参数有区间最大值,区间历史最大值,区间非最大值和区间非历史最大值这四个玩意要加上的数,同时要更新一堆东西。
-
\(sum\):区间最大值的个数乘上最大值加上的数,其他的数乘上非最大值加上的数。
-
\(mxhi\):首先还没有更新的 \(mxst\) 已经是历史了,所以我们要在原来的历史最大值和 \(mxst\) 加上历史最大值加上的数中取最大值。
-
\(mxst\):比较显然,直接加上最大值要加的数即可。
-
\(mxse\):如果这个区间有严格次大值,就加上非最大值加上的数。
-
\(lzyhi\):这时还没有更新的 \(lzyst\) 也已经成为了历史,所以按照 \(mxhi\) 的方式进行更新就行了。
-
\(lzyst\):直接加上最大值应该加的数即可。
-
\(lzyhe\):此时的 \(lzyse\) 也已经成为了历史,所以按照 \(lzyhi\) 的方式进行更新即可。
-
\(lzyse\):直接加上非最大值应该加上的数即可。
然后放一下代码:
void modify(int u,int val_st,int val_hi,int val_se,int val_he){
tr[u].sum+=(tr[u].mx_num*val_st+(tr[u].r-tr[u].l+1-tr[u].mx_num)*val_se);
tr[u].mx_hi=max(tr[u].mx_hi,tr[u].mx_st+val_hi);
tr[u].mx_st+=val_st;
if(tr[u].mx_se!=-inf)tr[u].mx_se+=val_se;
tr[u].lzy_hi=max(tr[u].lzy_hi,tr[u].lzy_st+val_hi);
tr[u].lzy_st+=val_st;
tr[u].lzy_he=max(tr[u].lzy_he,tr[u].lzy_se+val_he);
tr[u].lzy_se+=val_se;
}
好了,然后我们继续说 \(pushdown\):
首先我们记 \(mx\) 为当前区间的最大值,下面我们进行分情况讨论:
如果一个区间的最大值为 \(mx\),则我们直接按照把四个懒标记传进 \(modify\) 里即可。
否则,都不是最大值,那么我们把应该传进最大值和历史最大值懒标记的部分传进非最大值和历史非最大值的懒标记即可。
下面放一下代码:
void pushdown(int u){
int maxx=max(tr[u<<1].mx_st,tr[u<<1|1].mx_st);
if(tr[u<<1].mx_st==maxx)modify(u<<1,tr[u].lzy_st,tr[u].lzy_hi,tr[u].lzy_se,tr[u].lzy_he);
else modify(u<<1,tr[u].lzy_se,tr[u].lzy_he,tr[u].lzy_se,tr[u].lzy_he);
if(tr[u<<1|1].mx_st==maxx)modify(u<<1|1,tr[u].lzy_st,tr[u].lzy_hi,tr[u].lzy_se,tr[u].lzy_he);
else modify(u<<1|1,tr[u].lzy_se,tr[u].lzy_he,tr[u].lzy_se,tr[u].lzy_he);
tr[u].lzy_st=tr[u].lzy_hi=tr[u].lzy_se=tr[u].lzy_he=0;
}
区间加
非常好做,按照正常线段树的方式直接在区间被完全包含时进行 \(modify\) 即可,所以直接看一下代码就行了:
void modify_add(int u,int L,int R,int v){
if(tr[u].l>=L&&tr[u].r<=R){
modify(u,v,v,v,v);
return;
}
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if(L<=mid)modify_add(u<<1,L,R,v);
if(R>mid)modify_add(u<<1|1,L,R,v);
pushup(u);
}
区间取最小值
最后一个比较恶心的东西,考虑一个事情,当前区间最大值已经不比要修改的值大了直接返回就好,但是这样在修改的值极小的时候复杂度会直接炸掉,所以这时就是我们用上之前维护的严格次大值派上用场的时候了。
我们只需要找到 \(mxst< v < mxse\) 的区间修改区间的最大值和历史最大值即可,并且可以把取最小值的操作改成加法操作,所以直接使用 \(modify\) 改就好了。
然后放一下代码:
void modify_min(int u,int L,int R,int v){
if(tr[u].mx_st<=v)return;
if(tr[u].l>=L&&tr[u].r<=R&&tr[u].mx_se<v){
modify(u,v-tr[u].mx_st,v-tr[u].mx_st,0,0);
return;
}
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if(L<=mid)modify_min(u<<1,L,R,v);
if(R>mid)modify_min(u<<1|1,L,R,v);
pushup(u);
}
一堆查询
没啥好说的,按照正常线段树的查法查就行了,代码见最后的代码里。
代码(合集版)
#include<bits/stdc++.h>
#define int long long
#define N 500005
#define inf 2e18
using namespace std;
int n,m,a[N];
struct node{
int l,r,sum;
int mx_st,mx_se,mx_hi,mx_num;
int lzy_st,lzy_hi,lzy_se,lzy_he;
}tr[N<<2];
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
tr[u].mx_st=max(tr[u<<1].mx_st,tr[u<<1|1].mx_st);
tr[u].mx_hi=max(tr[u<<1].mx_hi,tr[u<<1|1].mx_hi);
if(tr[u<<1].mx_st==tr[u<<1|1].mx_st){
tr[u].mx_num=tr[u<<1].mx_num+tr[u<<1|1].mx_num;
tr[u].mx_se=max(tr[u<<1].mx_se,tr[u<<1|1].mx_se);
}
else if(tr[u<<1].mx_st>tr[u<<1|1].mx_st){
tr[u].mx_num=tr[u<<1].mx_num;
tr[u].mx_se=max(tr[u<<1].mx_se,tr[u<<1|1].mx_st);
}
else{
tr[u].mx_num=tr[u<<1|1].mx_num;
tr[u].mx_se=max(tr[u<<1].mx_st,tr[u<<1|1].mx_se);
}
}
void build(int u,int l,int r){
tr[u]={l,r};
if(l==r){
tr[u].sum=tr[u].mx_st=tr[u].mx_hi=a[l];
tr[u].mx_num=1;
tr[u].mx_se=-inf;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int val_st,int val_hi,int val_se,int val_he){
tr[u].sum+=(tr[u].mx_num*val_st+(tr[u].r-tr[u].l+1-tr[u].mx_num)*val_se);
tr[u].mx_hi=max(tr[u].mx_hi,tr[u].mx_st+val_hi);
tr[u].mx_st+=val_st;
if(tr[u].mx_se!=-inf)tr[u].mx_se+=val_se;
tr[u].lzy_hi=max(tr[u].lzy_hi,tr[u].lzy_st+val_hi);
tr[u].lzy_st+=val_st;
tr[u].lzy_he=max(tr[u].lzy_he,tr[u].lzy_se+val_he);
tr[u].lzy_se+=val_se;
}
void pushdown(int u){
int maxx=max(tr[u<<1].mx_st,tr[u<<1|1].mx_st);
if(tr[u<<1].mx_st==maxx)modify(u<<1,tr[u].lzy_st,tr[u].lzy_hi,tr[u].lzy_se,tr[u].lzy_he);
else modify(u<<1,tr[u].lzy_se,tr[u].lzy_he,tr[u].lzy_se,tr[u].lzy_he);
if(tr[u<<1|1].mx_st==maxx)modify(u<<1|1,tr[u].lzy_st,tr[u].lzy_hi,tr[u].lzy_se,tr[u].lzy_he);
else modify(u<<1|1,tr[u].lzy_se,tr[u].lzy_he,tr[u].lzy_se,tr[u].lzy_he);
tr[u].lzy_st=tr[u].lzy_hi=tr[u].lzy_se=tr[u].lzy_he=0;
}
void modify_add(int u,int L,int R,int v){
if(tr[u].l>=L&&tr[u].r<=R){
modify(u,v,v,v,v);
return;
}
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if(L<=mid)modify_add(u<<1,L,R,v);
if(R>mid)modify_add(u<<1|1,L,R,v);
pushup(u);
}
void modify_min(int u,int L,int R,int v){
if(tr[u].mx_st<=v)return;
if(tr[u].l>=L&&tr[u].r<=R&&tr[u].mx_se<v){
modify(u,v-tr[u].mx_st,v-tr[u].mx_st,0,0);
return;
}
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if(L<=mid)modify_min(u<<1,L,R,v);
if(R>mid)modify_min(u<<1|1,L,R,v);
pushup(u);
}
int qry_sum(int u,int L,int R){
if(tr[u].l>=L&&tr[u].r<=R){
return tr[u].sum;
}
int mid=tr[u].l+tr[u].r>>1;
int res=0;
pushdown(u);
if(L<=mid)res+=qry_sum(u<<1,L,R);
if(R>mid)res+=qry_sum(u<<1|1,L,R);
return res;
}
int qry_max(int u,int L,int R){
if(tr[u].l>=L&&tr[u].r<=R){
return tr[u].mx_st;
}
int mid=tr[u].l+tr[u].r>>1;
int res=-inf;
pushdown(u);
if(L<=mid)res=max(res,qry_max(u<<1,L,R));
if(R>mid)res=max(res,qry_max(u<<1|1,L,R));
return res;
}
int qry_hi(int u,int L,int R){
if(tr[u].l>=L&&tr[u].r<=R){
return tr[u].mx_hi;
}
int mid=tr[u].l+tr[u].r>>1;
int res=-inf;
pushdown(u);
if(L<=mid)res=max(res,qry_hi(u<<1,L,R));
if(R>mid)res=max(res,qry_hi(u<<1|1,L,R));
return res;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
while(m--){
int op,l,r,k,v;
cin>>op;
if(op==1){
cin>>l>>r>>k;
modify_add(1,l,r,k);
}
else if(op==2){
cin>>l>>r>>v;
modify_min(1,l,r,v);
}
else if(op==3){
cin>>l>>r;
cout<<qry_sum(1,l,r)<<'\n';
}
else if(op==4){
cin>>l>>r;
cout<<qry_max(1,l,r)<<'\n';
}
else{
cin>>l>>r;
cout<<qry_hi(1,l,r)<<'\n';
}
}
return 0;
}
标签:lzy,int,线段,tr,mx,司机,st,最大值
From: https://www.cnblogs.com/zxh923aoao/p/18302375