【数据结构】吉司机线段树
直接上例题
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。
- 参考别人代码的写法:
#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;
}
- 自己乱搞的写法:
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);
}

浙公网安备 33010602011771号