P6242-[模板]线段树3【吉司机线段树】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P6242


1|1题目大意

给出一个长度为n的序列am次要求支持操作

  1. 区间加上一个值k
  2. 区间所有ai变为min{ai,k}
  3. 区间求和
  4. 区间求最大值
  5. 区间求历史最大值

1n,q5×105


1|2解题思路

额让我们来看看都有些什么操作,区间加权,区间取min......

欸好像到区间取min就不会了,这时候就要请出我们的吉司机线段树了。

吉司机线段树的原理大概就是一个节点处多维护一个与有关的值,这样我们一次修改时就只需要以常数的代价减少线段树的来达到均摊复杂度的效果。

先考虑这题的一个弱化版,要求支持

  • 区间求和
  • 区间求min
  • 区间取min

此时我们可以将线段树的势视为一个区间的不同数个数,那么我们需要找到一种新的标记方法使得我们能够减少一点势能。

而对于区间取min,要求在标记改变一个数的情况下能维护区间和,显然的区间取min第一个改变的肯定是最大值,所以此时我们就可以对于一个位置多维护三个值:区间最大值mx区间最大值个数t区间次大值se

此时我们修改时如果取min的值k分为以下情况

  • kmx,此时不会对区间产生影响,势能不变。
  • mx>k>se,此时我们暴力修改mxt,并以此修改sum,势能不变。
  • kse,此时我们暴力递归左右两边修改,此时我们相当于多做了一次操作,但是mx变为了se相等的值,势能减一。

综上,由于初始序列的势能最大为O(n),这样的时间复杂度为O(nlogn)

然后我们又多了一个操作

  • 区间加上取值k

注意到这个操作最多会修改logn个区间,而每个被修改的区间都会产生多一个势能,时间复杂度就变为了O(nlog2n),不是很优秀,我们考虑更好的办法。

我们考虑分裂最大值的标记和次大值的标记,也就是我们的标记不再是区间减去的值,我们分裂为两个标记:区间最大值要减去的值lazyx区间非最大值要减去的值lazy

此时我们进行区间下传的时候非最大值的子区间就顺便处理了,如果两边都是最大值的子区间那么两边的势能都会减1,所以时间复杂度依旧是:O(nlogn)

然后注意到这题还有一个难点

  • 区间查询历史最大值

先考虑正常的线段树是如何处理这种情况的,我们可以多维护两个东西:历史最大值b上次更新后历史最大标记lazyb,下传时我们用lazy更新lazyb,再用w+lazyb更新b就好了。

那么同样的我们在上面套一个类似吉司机线段树的东西,维护两种标记:上次更新后最大值的最大减少值lazyxb上次更新后非最大值的最大减少值lazyb

然后用类似的方法维护就好了。

写起来超级麻烦

时间复杂度:O((m+n)logn)


1|3code

#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 */

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15704501.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(121)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示