P6242-[模板]线段树3【吉司机线段树】
正题
题目链接:https://www.luogu.com.cn/problem/P6242
题目大意
给出一个长度为\(n\)的序列\(a\),\(m\)次要求支持操作
- 区间加上一个值\(k\)
- 区间所有\(a_i\)变为\(min\{a_i,k\}\)
- 区间求和
- 区间求最大值
- 区间求历史最大值
\(1\leq n,q\leq 5\times 10^5\)
解题思路
额让我们来看看都有些什么操作,区间加权,区间取min......
欸好像到区间取min就不会了,这时候就要请出我们的吉司机线段树了。
吉司机线段树的原理大概就是一个节点处多维护一个与势有关的值,这样我们一次修改时就只需要以常数的代价减少线段树的势来达到均摊复杂度的效果。
先考虑这题的一个弱化版,要求支持
- 区间求和
- 区间求min
- 区间取min
此时我们可以将线段树的势视为一个区间的不同数个数,那么我们需要找到一种新的标记方法使得我们能够减少一点势能。
而对于区间取min,要求在标记改变一个数的情况下能维护区间和,显然的区间取min第一个改变的肯定是最大值,所以此时我们就可以对于一个位置多维护三个值:区间最大值mx,区间最大值个数t,区间次大值se。
此时我们修改时如果取min的值\(k\)分为以下情况
- \(k\geq mx\),此时不会对区间产生影响,势能不变。
- \(mx>k>se\),此时我们暴力修改\(mx\)和\(t\),并以此修改\(sum\),势能不变。
- \(k\geq se\),此时我们暴力递归左右两边修改,此时我们相当于多做了一次操作,但是\(mx\)变为了\(se\)相等的值,势能减一。
综上,由于初始序列的势能最大为\(O(n)\),这样的时间复杂度为\(O(n\log n)\)。
然后我们又多了一个操作
- 区间加上取值\(k\)
注意到这个操作最多会修改\(\log n\)个区间,而每个被修改的区间都会产生多一个势能,时间复杂度就变为了\(O(n\log^2n )\),不是很优秀,我们考虑更好的办法。
我们考虑分裂最大值的标记和次大值的标记,也就是我们的标记不再是区间减去的值,我们分裂为两个标记:区间最大值要减去的值lazyx,区间非最大值要减去的值lazy。
此时我们进行区间下传的时候非最大值的子区间就顺便处理了,如果两边都是最大值的子区间那么两边的势能都会减1,所以时间复杂度依旧是:\(O(n\log n)\)
然后注意到这题还有一个难点
- 区间查询历史最大值
先考虑正常的线段树是如何处理这种情况的,我们可以多维护两个东西:历史最大值b,上次更新后历史最大标记lazyb,下传时我们用\(lazy\)更新\(lazyb\),再用\(w+lazyb\)更新\(b\)就好了。
那么同样的我们在上面套一个类似吉司机线段树的东西,维护两种标记:上次更新后最大值的最大减少值lazyxb,上次更新后非最大值的最大减少值lazyb
然后用类似的方法维护就好了。
写起来超级麻烦
时间复杂度:\(O((m+n)\log n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=5e5+10,M=N<<2,inf=1e18;
ll n,m,sum[M],mxa[M],mxb[M],se[M],t[M];
ll la[M],lax[M],lb[M],lbx[M];
void Merge(ll x,ll L,ll R){
sum[x]=sum[x*2]+sum[x*2+1];
mxa[x]=max(mxa[x*2],mxa[x*2+1]);
mxb[x]=max(mxb[x*2],mxb[x*2+1]);
if(mxa[x*2]==mxa[x*2+1]){
t[x]=t[x*2]+t[x*2+1];
se[x]=max(se[x*2],se[x*2+1]);
}
else if(mxa[x*2]<mxa[x*2+1]){
t[x]=t[x*2+1];
se[x]=max(mxa[x*2],se[x*2+1]);
}
else if(mxa[x*2]>mxa[x*2+1]){
t[x]=t[x*2];
se[x]=max(se[x*2],mxa[x*2+1]);
}
return;
}
void Updata(ll x,ll len,ll a,ll b,ll c,ll d){
//a:非最大值加的值 b:最大值加的值
sum[x]+=a*(len-t[x])+b*t[x];
mxb[x]=max(mxb[x],mxa[x]+d);
lbx[x]=max(lbx[x],lax[x]+d);
lb[x]=max(lb[x],la[x]+c);
mxa[x]+=b;lax[x]+=b;la[x]+=a;
if(se[x]!=-inf)se[x]+=a;
}
void Downdata(ll x,ll L,ll R){
ll mid=(L+R)>>1,maxn=max(mxa[x*2],mxa[x*2+1]);
if(mxa[x*2]==maxn)
Updata(x*2,mid-L+1,la[x],lax[x],lb[x],lbx[x]);
else
Updata(x*2,mid-L+1,la[x],la[x],lb[x],lb[x]);
if(mxa[x*2+1]==maxn)
Updata(x*2+1,R-mid,la[x],lax[x],lb[x],lbx[x]);
else
Updata(x*2+1,R-mid,la[x],la[x],lb[x],lb[x]);
la[x]=lax[x]=lb[x]=lbx[x]=0;
return;
}
void ChangeAdd(ll x,ll L,ll R,ll l,ll r,ll val){
if(L==l&&R==r){Updata(x,R-L+1,val,val,val,val);return;}
ll mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)ChangeAdd(x*2,L,mid,l,r,val);
else if(l>mid)ChangeAdd(x*2+1,mid+1,R,l,r,val);
else ChangeAdd(x*2,L,mid,l,mid,val),ChangeAdd(x*2+1,mid+1,R,mid+1,r,val);
Merge(x,L,R);return;
}
void ChangeMin(ll x,ll L,ll R,ll l,ll r,ll val){
ll mid=(L+R)>>1;
if(val>=mxa[x])return;
if(L==l&&R==r){
if(val>se[x]){
Updata(x,R-L+1,0,val-mxa[x],0,val-mxa[x]);
return;
}
Downdata(x,L,R);
ChangeMin(x*2,L,mid,l,mid,val);
ChangeMin(x*2+1,mid+1,R,mid+1,r,val);
Merge(x,L,R);return;
}
Downdata(x,L,R);
if(r<=mid)ChangeMin(x*2,L,mid,l,r,val);
else if(l>mid)ChangeMin(x*2+1,mid+1,R,l,r,val);
else ChangeMin(x*2,L,mid,l,mid,val),ChangeMin(x*2+1,mid+1,R,mid+1,r,val);
Merge(x,L,R);
}
ll AskSum(ll x,ll L,ll R,ll l,ll r){
if(L==l&&R==r)return sum[x];
ll mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)return AskSum(x*2,L,mid,l,r);
if(l>mid)return AskSum(x*2+1,mid+1,R,l,r);
return AskSum(x*2,L,mid,l,mid)+AskSum(x*2+1,mid+1,R,mid+1,r);
}
ll AskMaxa(ll x,ll L,ll R,ll l,ll r){
if(L==l&&R==r)return mxa[x];
ll mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)return AskMaxa(x*2,L,mid,l,r);
if(l>mid)return AskMaxa(x*2+1,mid+1,R,l,r);
return max(AskMaxa(x*2,L,mid,l,mid),AskMaxa(x*2+1,mid+1,R,mid+1,r));
}
ll AskMaxb(ll x,ll L,ll R,ll l,ll r){
if(L==l&&R==r)return mxb[x];
ll mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)return AskMaxb(x*2,L,mid,l,r);
if(l>mid)return AskMaxb(x*2+1,mid+1,R,l,r);
return max(AskMaxb(x*2,L,mid,l,mid),AskMaxb(x*2+1,mid+1,R,mid+1,r));
}
void Build(ll x,ll L,ll R){
if(L==R){
ll w;scanf("%lld",&w);
mxa[x]=mxb[x]=sum[x]=w;
se[x]=-inf;t[x]=1;
return;
}
ll mid=(L+R)>>1;
Build(x*2,L,mid);
Build(x*2+1,mid+1,R);
Merge(x,L,R);
return;
}
signed main()
{
scanf("%lld%lld",&n,&m);
Build(1,1,n);
while(m--){
ll op,l,r,w;
scanf("%lld%lld%lld",&op,&l,&r);
if(op==1){scanf("%lld",&w);ChangeAdd(1,1,n,l,r,w);}
if(op==2){scanf("%lld",&w);ChangeMin(1,1,n,l,r,w);}
if(op==3)printf("%lld\n",AskSum(1,1,n,l,r));
if(op==4)printf("%lld\n",AskMaxa(1,1,n,l,r));
if(op==5)printf("%lld\n",AskMaxb(1,1,n,l,r));
}
return 0;
}
/*
5 6
1 2 3 4 5
2 1 5 3
3 1 5
*/