欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

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

发布时间:2023/12/3 35 豆豆
生活随笔 收集整理的这篇文章主要介绍了 P6242-[模板]线段树3【吉司机线段树】 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

正题

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


题目大意

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

  • 区间加上一个值kkk
  • 区间所有aia_iai变为min{ai,k}min\{a_i,k\}min{ai,k}
  • 区间求和
  • 区间求最大值
  • 区间求历史最大值
  • 1≤n,q≤5×1051\leq n,q\leq 5\times 10^51n,q5×105


    解题思路

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

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

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

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

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

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

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

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

    • k≥mxk\geq mxkmx,此时不会对区间产生影响,势能不变。
    • mx>k>semx>k>semx>k>se,此时我们暴力修改mxmxmxttt,并以此修改sumsumsum,势能不变。
    • k≥sek\geq sekse,此时我们暴力递归左右两边修改,此时我们相当于多做了一次操作,但是mxmxmx变为了sesese相等的值,势能减一。

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

    然后我们又多了一个操作

    • 区间加上取值kkk

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

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

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

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

    • 区间查询历史最大值

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

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

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

    写起来超级麻烦

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


    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]);elseUpdata(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]);elseUpdata(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 */

    总结

    以上是生活随笔为你收集整理的P6242-[模板]线段树3【吉司机线段树】的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。