吉司机线段树

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 监控

\(-\) ❀ 完结撒花 ❀ \(-\)

posted @ 2026-08-12 11:45  404-NoT_FinD  阅读(4)  评论(0)    收藏  举报