吉司机线段树
HDU5306 Gorgeous Sequence
前置知识:带懒标记的线段树。
题目大意
对给定序列实现在线区间取 \(min\)、区间求和以及区间求最大值。
其中 “区间取 \(min\)” 指给定区间和数 \(x\),则区间内所有大于 \(x\) 的数都替换为 \(x\)。
\(Solution\)
很线段树,但是区间取 \(min\) 不好实现。
于是一个名为 “吉司机线段树” 的东西横空出世。
需要记录区间最大值 \(maxval\)、区间严格次大值 \(sndmax\)。
在区间对 \(x\) 取 \(min\) 时,只要考虑以下三种情况:
- 若 \(x\) \(\ge\) \(maxval\),则该区间内所有数都小于 \(x\),取 \(min\) 后不会发生改变,直接 \(return\) 掉就好;
- 若 \(maxval\) \(>\) \(x\) \(>\) \(sndval\),则除了 \(maxval\) 以外,其他元素都不需要修改;
- 若 \(maxval\) \(>\) \(sndval\) \(\ge\) \(x\),那么这种情况在当前区间内无法处理,故甩给左右两个子区间即可。
需要注意:第二种情况中确实是 \(x\) \(>\) \(sndval\),没有打错。
这是因为如果把等于的情况也纳入的话,将 \(maxval\) 更新为 \(x\) 后就无法保证 \(sndmax\) 为严格次大值了。
那么如何处理第二种情况呢?
其实只需要更新 \(maxval\) 为 \(x\)、将区间 \(sum\) 减去 \(maxcnt*maxval-x\) 即可。
其中 \(maxcnt\) 为最大值出现次数,毕竟有可能有很多个最大值。
以上就是吉司机线段树处理 “区间取 \(min\)” 的核心思路,剩下的与普通线段树大致相同,并不难以理解。
记得要用懒标记哟,记下最大值加减以及非最大值加减的懒标记。
那么我将呈上一份史山代码。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
long long a[1000005];
struct Node{
int l,r;
long long maxval,maxcnt,maxtag,sndmax,sndtag,sum;
};
Node tr[4000005];
inline void Updata(int id,long long k,long long t){
tr[id].sum+=tr[id].maxcnt*k+(tr[id].r-tr[id].l+1ll-tr[id].maxcnt)*t;
tr[id].maxval+=k,tr[id].sndmax+=t;
tr[id].maxtag+=k,tr[id].sndtag+=t;
return;
}
inline void Push(int id){
Node &lson=tr[id*2],&rson=tr[id*2+1],&father=tr[id];
long long lsmax=max(lson.maxval,rson.maxval);
if(lson.maxval==lsmax) Updata(id*2,father.maxtag,father.sndtag);
else Updata(id*2,father.sndtag,father.sndtag);
if(rson.maxval==lsmax) Updata(id*2+1,father.maxtag,father.sndtag);
else Updata(id*2+1,father.sndtag,father.sndtag);
father.maxtag=father.sndtag=0;
return;
}
inline void Pushup(int id){
Node lson=tr[id*2],rson=tr[id*2+1];
long long lsmax=max(lson.maxval,rson.maxval);
tr[id].sum=lson.sum+rson.sum;
tr[id].maxval=max(lson.maxval,rson.maxval);
tr[id].maxcnt=(lson.maxval==rson.maxval?lson.maxcnt+rson.maxcnt:(lson.maxval>rson.maxval?lson.maxcnt:rson.maxcnt));
tr[id].sndmax=(lson.maxval==rson.maxval?max(lson.sndmax,rson.sndmax):(lson.maxval>rson.maxval?max(lson.sndmax,rson.maxval):max(rson.sndmax,lson.maxval)));
return;
}
inline void Build(int id,int left,int right){
tr[id].l=left,tr[id].r=right;
if(left==right){
tr[id].maxval=tr[id].sum=a[left],tr[id].maxcnt=1;
tr[id].sndmax=-1e18;
return;
}
int mid=(left+right)/2;
Build(id*2,left,mid),Build(id*2+1,mid+1,right);
Pushup(id);
return;
}
inline void Modify(int id,int left,int right,int k){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return;
if(left<=L&&R<=right){
Updata(id,k,k);
return;
}
Push(id);
Modify(id*2,left,right,k),Modify(id*2+1,left,right,k);
Pushup(id);
return;
}
inline void Changemin(int id,int left,int right,int k){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left||tr[id].maxval<=k) return;
if(left<=L&&right>=R&&tr[id].sndmax<k){
Updata(id,k-tr[id].maxval,0);
return;
}
if(L==R){
if(tr[id].maxval>k) tr[id].maxval=k,tr[id].sum=k;
return;
}
Push(id);
Changemin(id*2,left,right,k),Changemin(id*2+1,left,right,k);
Pushup(id);
return;
}
long long Query(int id,int left,int right){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return 0;
if(left<=L&&right>=R) return tr[id].sum;
Push(id);
long long ans=Query(id*2,left,right)+Query(id*2+1,left,right);
Pushup(id);
return ans;
}
long long Getmax(int id,int left,int right){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return -1e18;
if(left<=L&&right>=R) return tr[id].maxval;
Push(id);
long long ans=max(Getmax(id*2,left,right),Getmax(id*2+1,left,right));
return ans;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
Build(1,1,n);
for(int i=1;i<=m;i++){
int opt,L,R,t;
scanf("%d%d%d",&opt,&L,&R);
if(opt==0) scanf("%d",&t),Changemin(1,L,R,t);
else if(opt==1) printf("%lld\n",Getmax(1,L,R));
else printf("%lld\n",Query(1,L,R));
}
return 0;
}
为什么这么宽啊......
P6242 【模板】线段树 3(区间最值操作、区间历史最值)
恭喜你已经学会了非常常规的吉司机线段树。
接下来是愉快的练习时间~
题目大意
给定区间,在线实现区间加、区间取 \(min\)、区间求和、查询区间最大值、查群区间历史最大值。
什么史......
\(Solution\)
仅仅只是比上一道题多出一个区间历史最大值而已。
只需要多维护区间历史最大值、历史最大值的最大增量以及非历史最大值的最大增量。
好绕口啊......
\(Code\)
#include<bits/stdc++.h>
using namespace std;
long long a[1000005];
struct Node{
int l,r;
long long maxval,maxcnt,sndmax,hismax,sum;
long long sndtag,maxtag,hmxtag,nhmtag;
};
Node tr[4000005];
inline void Updata(int id,long long k,long long t,long long hmxt,long long nhmt){
tr[id].sum+=tr[id].maxcnt*k+(tr[id].r-tr[id].l+1ll-tr[id].maxcnt)*t;
tr[id].hismax=max(tr[id].hismax,tr[id].maxval+hmxt);
tr[id].maxval+=k,tr[id].sndmax+=t;
tr[id].hmxtag=max(tr[id].hmxtag,tr[id].maxtag+hmxt);
tr[id].maxtag+=k;
tr[id].nhmtag=max(tr[id].nhmtag,tr[id].sndtag+nhmt);
tr[id].sndtag+=t;
return;
}
inline void Push(int id){
Node &lson=tr[id*2],&rson=tr[id*2+1],&father=tr[id];
long long lsmax=max(lson.maxval,rson.maxval);
if(lson.maxval==lsmax) Updata(id*2,father.maxtag,father.sndtag,father.hmxtag,father.nhmtag);
else Updata(id*2,father.sndtag,father.sndtag,father.nhmtag,father.nhmtag);
if(rson.maxval==lsmax) Updata(id*2+1,father.maxtag,father.sndtag,father.hmxtag,father.nhmtag);
else Updata(id*2+1,father.sndtag,father.sndtag,father.nhmtag,father.nhmtag);
father.maxtag=father.sndtag=father.hmxtag=father.nhmtag=0;
return;
}
inline void Pushup(int id){
Node lson=tr[id*2],rson=tr[id*2+1];
long long lsmax=max(lson.maxval,rson.maxval);
tr[id].sum=lson.sum+rson.sum;
tr[id].maxval=max(lson.maxval,rson.maxval);
tr[id].hismax=max(lson.hismax,rson.hismax);
tr[id].maxcnt=(lson.maxval==rson.maxval?lson.maxcnt+rson.maxcnt:(lson.maxval>rson.maxval?lson.maxcnt:rson.maxcnt));
tr[id].sndmax=(lson.maxval==rson.maxval?max(lson.sndmax,rson.sndmax):(lson.maxval>rson.maxval?max(lson.sndmax,rson.maxval):max(rson.sndmax,lson.maxval)));
return;
}
inline void Build(int id,int left,int right){
tr[id].l=left,tr[id].r=right;
if(left==right){
tr[id].maxval=tr[id].hismax=tr[id].sum=a[left];
tr[id].maxcnt=1,tr[id].sndmax=-1e18;
return;
}
int mid=(left+right)/2;
Build(id*2,left,mid),Build(id*2+1,mid+1,right);
Pushup(id);
return;
}
inline void Modify(int id,int left,int right,int k){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return;
if(left<=L&&R<=right){
Updata(id,k,k,k,k);
return;
}
Push(id);
Modify(id*2,left,right,k),Modify(id*2+1,left,right,k);
Pushup(id);
return;
}
inline void Changemin(int id,int left,int right,int k){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left||tr[id].maxval<=k) return;
if(left<=L&&right>=R&&tr[id].sndmax<k){
Updata(id,k-tr[id].maxval,0,k-tr[id].maxval,0);
return;
}
if(L==R){
if(tr[id].maxval>k) tr[id].maxval=k,tr[id].sum=k;
return;
}
Push(id);
Changemin(id*2,left,right,k),Changemin(id*2+1,left,right,k);
Pushup(id);
return;
}
inline long long Query(int id,int left,int right){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return 0;
if(left<=L&&right>=R) return tr[id].sum;
Push(id);
long long ans=Query(id*2,left,right)+Query(id*2+1,left,right);
Pushup(id);
return ans;
}
inline long long Getmax(int id,int left,int right){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return -1e18;
if(left<=L&&right>=R) return tr[id].maxval;
Push(id);
long long ans=max(Getmax(id*2,left,right),Getmax(id*2+1,left,right));
return ans;
}
inline long long Gethis(int id,int left,int right){
int &L=tr[id].l,&R=tr[id].r;
if(right<L||R<left) return -1e18;
if(left<=L&&right>=R) return tr[id].hismax;
Push(id);
long long ans=max(Gethis(id*2,left,right),Gethis(id*2+1,left,right));
return ans;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
Build(1,1,n);
for(int i=1;i<=m;i++){
int opt,L,R,t;
scanf("%d%d%d",&opt,&L,&R);
if(opt==1) scanf("%d",&t),Modify(1,L,R,t);
else if(opt==2) scanf("%d",&t),Changemin(1,L,R,t);
else if(opt==3) printf("%lld\n",Query(1,L,R));
else if(opt==4) printf("%lld\n",Getmax(1,L,R));
else printf("%lld\n",Gethis(1,L,R));
}
return 0;
}
练习
P10639 BZOJ4695 最假女选手
P4314 CPU 监控
\(-\) ❀ 完结撒花 ❀ \(-\)

浙公网安备 33010602011771号