吉司机线段树学习笔记
问题
给你一个序列,要求在原线段树的基础上加入以下两种操作:
- 将 \(l\sim r\) 中的所有 \(a_i\gets \min(a_i,x)\)(或者 \(\max\))
- 求 \(l\sim r\) 中所有 \(a_i\) 的历史最值的最值
吉司机线段树
注意到如果一个区间内有多种数 \(\ge x\),那么非常不好维护,但是如果只有一种数就很好维护。
而且我们注意到这种数一定是最大值。
那么我们只需要维护最大值(记为 \(mx\)),严格次大值(记为 \(se\),用来判断是否只有一种数 \(\ge x\)),最大值的出现次数(记为 \(cnt\),用来更新区间和)还有一个 \(tag\) 表示更新最大值就可以了。
具体步骤如下:
- 如果 \(mx\le x\),那么不用更新
- 如果 \(se<x<mx\) 且这个区间被目标区间包含,那么直接改变最大值和区间和,然后标记一个 \(tag\) 即可。(注意到 \(se\le x\) 不行,因为会改变 \(se\))
- 否则递归进入左右儿子进行操作
复杂度分析
我也不会,但是是 \(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;
}

浙公网安备 33010602011771号