吉司机线段树学习笔记

问题

给你一个序列,要求在原线段树的基础上加入以下两种操作:

  1. \(l\sim r\) 中的所有 \(a_i\gets \min(a_i,x)\)(或者 \(\max\)
  2. \(l\sim r\) 中所有 \(a_i\) 的历史最值的最值

吉司机线段树

注意到如果一个区间内有多种数 \(\ge x\),那么非常不好维护,但是如果只有一种数就很好维护。
而且我们注意到这种数一定是最大值。
那么我们只需要维护最大值(记为 \(mx\)),严格次大值(记为 \(se\),用来判断是否只有一种数 \(\ge x\)),最大值的出现次数(记为 \(cnt\),用来更新区间和)还有一个 \(tag\) 表示更新最大值就可以了。
具体步骤如下:

  1. 如果 \(mx\le x\),那么不用更新
  2. 如果 \(se<x<mx\) 且这个区间被目标区间包含,那么直接改变最大值和区间和,然后标记一个 \(tag\) 即可。(注意到 \(se\le x\) 不行,因为会改变 \(se\)
  3. 否则递归进入左右儿子进行操作

复杂度分析

我也不会,但是是 \(O(mlog^2n)\) 的。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct segment{
	int l , r , sum , se , cnt , maxa , maxb;
	int add1 , add2 , add3 , add4;
}tree[2000005];
int n , m;
void push_up(int i){
	tree[i].sum = tree[i<<1].sum + tree[i<<1|1].sum , tree[i].maxa = max(tree[i<<1].maxa,tree[i<<1|1].maxa);
	tree[i].maxb = max(tree[i<<1].maxb,tree[i<<1|1].maxb);
	if(tree[i<<1].maxa==tree[i<<1|1].maxa){
		tree[i].cnt = tree[i<<1].cnt + tree[i<<1|1].cnt , tree[i].se = max(tree[i<<1].se,tree[i<<1|1].se);
	}else if(tree[i<<1].maxa>tree[i<<1|1].maxa){
		tree[i].cnt = tree[i<<1].cnt , tree[i].se = max(tree[i<<1].se,tree[i<<1|1].maxa);
	}else{
		tree[i].cnt = tree[i<<1|1].cnt , tree[i].se = max(tree[i<<1|1].se,tree[i<<1].maxa);
	}
}
void change(int i , int k1 , int k2 , int k3 , int k4){
	tree[i].sum += k1 * tree[i].cnt + k2 * (tree[i].r - tree[i].l + 1 - tree[i].cnt);
	tree[i].maxb = max(tree[i].maxb,tree[i].maxa+k3) , tree[i].maxa += k1;
	if(tree[i].se!=-2e9)tree[i].se += k2;
	tree[i].add3 = max(tree[i].add1+k3,tree[i].add3);
	tree[i].add4 = max(tree[i].add2+k4,tree[i].add4);
	tree[i].add1 += k1 , tree[i].add2 += k2;
}
void push_down(int i){
	int maxn = max(tree[i<<1].maxa,tree[i<<1|1].maxa);
	if(tree[i<<1].maxa==maxn)change(i<<1,tree[i].add1,tree[i].add2,tree[i].add3,tree[i].add4);
	else change(i<<1,tree[i].add2,tree[i].add2,tree[i].add4,tree[i].add4);
	if(tree[i<<1|1].maxa==maxn)change(i<<1|1,tree[i].add1,tree[i].add2,tree[i].add3,tree[i].add4);
	else change(i<<1|1,tree[i].add2,tree[i].add2,tree[i].add4,tree[i].add4);
	tree[i].add1 = tree[i].add2 = tree[i].add3 = tree[i].add4 = 0;
}
void build(int i , int l , int r){
	tree[i].l = l , tree[i].r = r;
	if(l==r){
		cin >> tree[i].sum;tree[i].maxa = tree[i].maxb = tree[i].sum;
		tree[i].se = -2e9 , tree[i].cnt = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(i<<1,l,mid) , build(i<<1|1,mid+1,r);
	push_up(i);
}
void update_add(int i , int l , int r , int k){
	if(tree[i].l>=l&&tree[i].r<=r){
		tree[i].sum += (tree[i].r - tree[i].l + 1) * k , tree[i].maxa += k , tree[i].maxb = max(tree[i].maxa,tree[i].maxb);
		if(tree[i].se!=-2e9)tree[i].se += k;
		tree[i].add1 += k , tree[i].add2 += k;
		tree[i].add3 = max(tree[i].add3,tree[i].add1) , tree[i].add4 = max(tree[i].add4,tree[i].add2);
		return;
	}
	push_down(i);
	if(tree[i<<1].r>=l)update_add(i<<1,l,r,k);
	if(tree[i<<1|1].l<=r)update_add(i<<1|1,l,r,k);
	push_up(i);
}
void update_min(int i , int l , int r , int k){
	if(tree[i].maxa<=k)return;
	else if(k>tree[i].se&&tree[i].l>=l&&tree[i].r<=r){
		tree[i].sum -= (tree[i].maxa - k) * tree[i].cnt;
		tree[i].add1 -= (tree[i].maxa - k) , tree[i].maxa = k;
	}else{
		push_down(i);
		if(tree[i<<1].r>=l)update_min(i<<1,l,r,k);
		if(tree[i<<1|1].l<=r)update_min(i<<1|1,l,r,k);
		push_up(i);
	}
}
int query_sum(int i , int l , int r){
	if(tree[i].l>=l&&tree[i].r<=r)return tree[i].sum;
	push_down(i);int sum = 0;
	if(tree[i<<1].r>=l)sum += query_sum(i<<1,l,r);
	if(tree[i<<1|1].l<=r)sum += query_sum(i<<1|1,l,r);
	return sum;
}
int query_maxa(int i , int l , int r){
	if(tree[i].l>=l&&tree[i].r<=r)return tree[i].maxa;
	push_down(i);int sum = -2e9;
	if(tree[i<<1].r>=l)sum = max(sum,query_maxa(i<<1,l,r));
	if(tree[i<<1|1].l<=r)sum = max(sum,query_maxa(i<<1|1,l,r));
	return sum;
}
int query_maxb(int i , int l , int r){
	if(tree[i].l>=l&&tree[i].r<=r)return tree[i].maxb;
	push_down(i);int sum = -2e9;
	if(tree[i<<1].r>=l)sum = max(sum,query_maxb(i<<1,l,r));
	if(tree[i<<1|1].l<=r)sum = max(sum,query_maxb(i<<1|1,l,r));
	return sum;
}
signed main(){
	cin >> n >> m;
	build(1,1,n);
	while(m--){
		int op;cin >> op;
		if(op==1){
			int l , r , k;cin >> l >> r >> k;
			update_add(1,l,r,k);
		}else if(op==2){
			int l , r , v;cin >> l >> r >> v;
			update_min(1,l,r,v);
		}else if(op==3){
			int l , r;cin >> l >> r;
			cout << query_sum(1,l,r) << "\n";
		}else if(op==4){
			int l , r;cin >> l >> r;
			cout << query_maxa(1,l,r) << "\n";
		}else if(op==5){
			int l , r;cin >> l >> r;
			cout << query_maxb(1,l,r) << "\n";
		}
	}
    return 0;
}
posted @ 2026-07-15 09:13  虚空远行者  阅读(9)  评论(0)    收藏  举报