【数据结构】吉司机线段树

直接上例题

P6242 【模板】线段树 3(区间最值操作、区间历史最值)

解析

操作 \(1,3,4\) 是常见的,我们来看操作 \(2\)\(5\)

对于操作 \(5\),根据 \(B_i\) 的更新机制,其相当于求 \(A_i\) 的历史最大值。而在不考虑操作 \(2\) 的情况下,能影响 \(A_i\) 的只有操作 \(1\) 的区间加,处理区间加我们会维护一个加法懒标记 \(tag\),类似地,可以通过维护一个历史最大懒标记的懒标记 \(htag\) 来实现 \(B_i\) 的区间加历史最大值,从而得到 \(A_i\) 的历史最大值。

对于操作 \(2\),先考虑 \(A_i\) 怎么变。若要对 \(x\)\(\min\),则应当是把大于 \(x\) 的部分全部削平。然而直接削无法维护区间和,对此的解决办法是一点一点削。具体地,我们维护区间最大值 \(mx\),最大值个数 \(cnt\) 和严格次大值 \(smx\)。削的时候只削最大值,如果 \(x \ge mx\) 就什么都不做,若 \(smx< x < mx\) 就将 \(mx\) 削成 \(x\),若 \(x \le smx\) 就往下递归。

削的过程可以视作是区间减以便于更新 \(tag\)\(htag\)。注意由于削操作是只针对区间最大值的,所以对
于其余值,需要另外维护懒标记。

总的来说,需要维护如下信息:

  • \(sum\) 代表区间和。
  • \(cnt\) 代表区间最大值出现次数。
  • \(mx\) 代表区间最大值。
  • \(smx\) 代表区间次大值。
  • \(hmx\) 代表区间历史最大值。
  • \(tag\) 代表作用于区间最大值的加标记。
  • \(otag\) 代表作用于区间其余值的加标记。
  • \(htag\) 代表作用于区间最大值的加标记的最大值。
  • \(hotag\) 代表作用于区间其余值的加标记的最大值。

定义打标记函数为 add_tag,那么有一种实现方法是这样的:

void add_tag(int p,int l,int r,int t,int ht,int ot,int hot){
	htag[p] = max(htag[p],tag[p] + ht);
	hotag[p] = max(hotag[p],otag[p] + hot);
	hmx[p] = max(hmx[p],mx[p] + ht);
	tag[p] += t;
	otag[p] += ot;
	mx[p] += t;
	smx[p] += ot;
	sum[p] += cnt[p] * t + (r - l + 1 - cnt[p]) * ot;
}

其中 \(t\) 代表针对最大值的加标记。\(ht\) 代表针对最大值的加标记的最大值,具体的,是自从上次标记下传以来,标记在此期间变化达到的最大值。\(ot,hot\) 则是针对其余值的标记。

在不考虑操作 \(1\) 的情况下,这样做的总复杂度是 \(O(m\log n)\) 的,有了区间加则是 \(O(m\log^2 n)\) 的。具体证明读者可以自行查阅资料。

代码

这里给出两种写法,主要差异在于 add_tag

  1. 参考别人代码的写法:
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
const int N = 5e5 + 5;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned ui;
ll sum[N << 2],cnt[N << 2],mx[N << 2],hmx[N << 2],smx[N << 2],tag[N << 2],otag[N << 2],htag[N << 2],hotag[N << 2];
int a[N];
ll b[N];
void push_up(int p){
	sum[p] = sum[ls(p)] + sum[rs(p)];
	mx[p] = max(mx[ls(p)],mx[rs(p)]);
	hmx[p] = max(hmx[ls(p)],hmx[rs(p)]);
	if(mx[ls(p)] == mx[rs(p)]){
		smx[p] = max(smx[ls(p)],smx[rs(p)]);
		cnt[p] = cnt[ls(p)] + cnt[rs(p)];
	}else{
		smx[p] = max({smx[ls(p)],smx[rs(p)],min(mx[ls(p)],mx[rs(p)])});
		cnt[p] = mx[ls(p)] > mx[rs(p)] ? cnt[ls(p)] : cnt[rs(p)];
	}
}
void build(int p,int l,int r){
	if(l == r){
		sum[p] = mx[p] = hmx[p] = a[l];
		smx[p] = -1e17;
		cnt[p] = 1; 
		return;
	}
	build(ls(p),l,mid),build(rs(p),mid + 1,r);
	push_up(p);
}
void add_tag(int p,int l,int r,int t,int ht,int ot,int hot){
	htag[p] = max(htag[p],tag[p] + ht);
	hotag[p] = max(hotag[p],otag[p] + hot);
	hmx[p] = max(hmx[p],mx[p] + ht);
	tag[p] += t;
	otag[p] += ot;
	mx[p] += t;
	smx[p] += ot;
	sum[p] += cnt[p] * t + (r - l + 1 - cnt[p]) * ot;
}
void push_down(int p,int l,int r){ 
	bool fl = mx[ls(p)] == max(mx[ls(p)],mx[rs(p)]),fr = mx[rs(p)] == max(mx[ls(p)],mx[rs(p)]);
	if(fl) add_tag(ls(p),l,mid,tag[p],htag[p],otag[p],hotag[p]);
	else add_tag(ls(p),l,mid,otag[p],hotag[p],otag[p],hotag[p]);
	if(fr) add_tag(rs(p),mid + 1,r,tag[p],htag[p],otag[p],hotag[p]);
	else add_tag(rs(p),mid + 1,r,otag[p],hotag[p],otag[p],hotag[p]);
	tag[p] = otag[p] = htag[p] = hotag[p] = 0;
}
void add(int p,int l,int r,int L,int R,int k){
	if(l > R || r < L) return;
	if(l >= L && r <= R){
		add_tag(p,l,r,k,k,k,k);
		return;
	}
	push_down(p,l,r);
	add(ls(p),l,mid,L,R,k),add(rs(p),mid + 1,r,L,R,k);
	push_up(p);
}
void modi(int p,int l,int r,int L,int R,int k){
	if(l > R || r < L) return;
	if(l >= L && r <= R){
		if(smx[p] < k && k < mx[p]){
			add_tag(p,l,r,k - mx[p],k - mx[p],0,0);
		}else if(k <= smx[p]){
			push_down(p,l,r);
			modi(ls(p),l,mid,L,R,k),modi(rs(p),mid + 1,r,L,R,k);
			push_up(p);
		}
		return;
	}
	push_down(p,l,r);
	modi(ls(p),l,mid,L,R,k),modi(rs(p),mid + 1,r,L,R,k);
	push_up(p);
}
ll get_sum(int p,int l,int r,int L,int R){
	if(l > R || r < L) return 0;
	if(l >= L && r <= R){
		return sum[p];
	}
	push_down(p,l,r);
	return get_sum(ls(p),l,mid,L,R) + get_sum(rs(p),mid + 1,r,L,R);
}
ll get_mx(int p,int l,int r,int L,int R,ll *mx){
	if(l > R || r < L) return -1e17;
	if(l >= L && r <= R){
		return mx[p];
	}
	push_down(p,l,r);
	return max(get_mx(ls(p),l,mid,L,R,mx),get_mx(rs(p),mid + 1,r,L,R,mx));
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout); 
	int n,m;
	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>>l>>r;
		if(op == 1){
			cin>>k;
			add(1,1,n,l,r,k);
		}else if(op == 2){
			cin>>v;
			modi(1,1,n,l,r,v);
		}else if(op == 3){
			ll res = get_sum(1,1,n,l,r);
			cout<<res<<'\n';
		}else if(op == 4){
			ll res = get_mx(1,1,n,l,r,mx);
			cout<<res<<'\n';
		}else{
			ll res = get_mx(1,1,n,l,r,hmx);
			cout<<res<<'\n';
		}
	}
	return 0;
}
  1. 自己乱搞的写法:
void push_up(int p){
	sum[p] = sum[ls(p)] + sum[rs(p)];
	if(mx[ls(p)] == mx[rs(p)]){
		cnt[p] = cnt[ls(p)] + cnt[rs(p)];
	}else{
		cnt[p] = mx[ls(p)] > mx[rs(p)] ? cnt[ls(p)] : cnt[rs(p)];
	}
	mx[p] = max(mx[ls(p)],mx[rs(p)]);
	hmx[p] = max(hmx[ls(p)],hmx[rs(p)]);
	b[0] = mx[ls(p)],b[1] = smx[ls(p)],b[2] = mx[rs(p)],b[3] = smx[rs(p)],b[4] = -1e17;
	sort(b,b + 4,greater<ll>());
	for(int i=1;i<=4;i++){
		if(b[i] != b[0]){
			smx[p] = b[i];
			break;
		}
	}
}
void add_tag(int p,int l,int r,int fa,ll k,int op,int op2){
	if(op2){
		ll old = otag[p];
		otag[p] += otag[fa] + k;
		hotag[p] = max(hotag[p],old + hotag[fa] + k);
		smx[p] += otag[fa] + k;
		sum[p] += (r - l + 1 - cnt[p]) * (otag[fa] + k);
	}
	if(op == 1){
		ll old = tag[p];
		tag[p] += tag[fa] + k;
		htag[p] = max(htag[p],old + htag[fa] + k);
		hmx[p] = max(hmx[p],mx[p] - old + htag[p]);
		mx[p] += tag[fa] + k;
		sum[p] += cnt[p] * (tag[fa] + k);
	}else if(op == 2){
		ll old = tag[p];
		tag[p] += otag[fa] + k;
		htag[p] = max(htag[p],old + hotag[fa] + k);
		hmx[p] = max(hmx[p],mx[p] - old + htag[p]);
		mx[p] += otag[fa] + k;
		sum[p] += cnt[p] * (otag[fa] + k);
	}
}
void push_down(int p,int l,int r){ 
	bool fl = mx[ls(p)] == max(mx[ls(p)],mx[rs(p)]),fr = mx[rs(p)] == max(mx[ls(p)],mx[rs(p)]);
	add_tag(ls(p),l,mid,p,0,fl ? 1 : 2,1);
	add_tag(rs(p),mid + 1,r,p,0,fr ? 1 : 2,1);
	tag[p] = otag[p] = htag[p] = hotag[p] = 0;
}
void add(int p,int l,int r,int L,int R,int k){
	if(l > R || r < L) return;
	if(l >= L && r <= R){
		add_tag(p,l,r,0,k,1,1);
		return;
	}
	push_down(p,l,r);
	add(ls(p),l,mid,L,R,k),add(rs(p),mid + 1,r,L,R,k);
	push_up(p);
}
void modi(int p,int l,int r,int L,int R,int k){
	if(l > R || r < L) return;
	if(l >= L && r <= R){
		if(smx[p] < k && k < mx[p]){
			add_tag(p,l,r,0,k - mx[p],1,0);
		}else if(k <= smx[p]){
			push_down(p,l,r);
			modi(ls(p),l,mid,L,R,k),modi(rs(p),mid + 1,r,L,R,k);
			push_up(p);
		}
		return;
	}
	push_down(p,l,r);
	modi(ls(p),l,mid,L,R,k),modi(rs(p),mid + 1,r,L,R,k);
	push_up(p);
}
posted @ 2026-09-07 10:56  yutar  阅读(11)  评论(0)    收藏  举报