日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2023/12/3 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 P6242-[模板]线段树3【吉司机线段树】 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

正題

題目鏈接:https://www.luogu.com.cn/problem/P6242


題目大意

給出一個長度為nnn的序列aaammm次要求支持操作

  • 區(qū)間加上一個值kkk
  • 區(qū)間所有aia_iai?變?yōu)?span id="ozvdkddzhkzd" class="katex--inline">min{ai,k}min\{a_i,k\}min{ai?,k}
  • 區(qū)間求和
  • 區(qū)間求最大值
  • 區(qū)間求歷史最大值
  • 1≤n,q≤5×1051\leq n,q\leq 5\times 10^51n,q5×105


    解題思路

    額讓我們來看看都有些什么操作,區(qū)間加權(quán),區(qū)間取min…

    欸好像到區(qū)間取min就不會了,這時候就要請出我們的吉司機線段樹了。

    吉司機線段樹的原理大概就是一個節(jié)點處多維護一個與有關(guān)的值,這樣我們一次修改時就只需要以常數(shù)的代價減少線段樹的來達到均攤復(fù)雜度的效果。

    先考慮這題的一個弱化版,要求支持

    • 區(qū)間求和
    • 區(qū)間求min
    • 區(qū)間取min

    此時我們可以將線段樹的勢視為一個區(qū)間的不同數(shù)個數(shù),那么我們需要找到一種新的標記方法使得我們能夠減少一點勢能。

    而對于區(qū)間取min,要求在標記改變一個數(shù)的情況下能維護區(qū)間和,顯然的區(qū)間取min第一個改變的肯定是最大值,所以此時我們就可以對于一個位置多維護三個值:區(qū)間最大值mx區(qū)間最大值個數(shù)t區(qū)間次大值se。

    此時我們修改時如果取min的值kkk分為以下情況

    • k≥mxk\geq mxkmx,此時不會對區(qū)間產(chǎn)生影響,勢能不變。
    • mx>k>semx>k>semx>k>se,此時我們暴力修改mxmxmxttt,并以此修改sumsumsum,勢能不變。
    • k≥sek\geq sekse,此時我們暴力遞歸左右兩邊修改,此時我們相當于多做了一次操作,但是mxmxmx變?yōu)榱?span id="ozvdkddzhkzd" class="katex--inline">sesese相等的值,勢能減一。

    綜上,由于初始序列的勢能最大為O(n)O(n)O(n),這樣的時間復(fù)雜度為O(nlog?n)O(n\log n)O(nlogn)

    然后我們又多了一個操作

    • 區(qū)間加上取值kkk

    注意到這個操作最多會修改log?n\log nlogn個區(qū)間,而每個被修改的區(qū)間都會產(chǎn)生多一個勢能,時間復(fù)雜度就變?yōu)榱?span id="ozvdkddzhkzd" class="katex--inline">O(nlog?2n)O(n\log^2n )O(nlog2n),不是很優(yōu)秀,我們考慮更好的辦法。

    我們考慮分裂最大值的標記和次大值的標記,也就是我們的標記不再是區(qū)間減去的值,我們分裂為兩個標記:區(qū)間最大值要減去的值lazyx區(qū)間非最大值要減去的值lazy。

    此時我們進行區(qū)間下傳的時候非最大值的子區(qū)間就順便處理了,如果兩邊都是最大值的子區(qū)間那么兩邊的勢能都會減1,所以時間復(fù)雜度依舊是:O(nlog?n)O(n\log n)O(nlogn)

    然后注意到這題還有一個難點

    • 區(qū)間查詢歷史最大值

    先考慮正常的線段樹是如何處理這種情況的,我們可以多維護兩個東西:歷史最大值b上次更新后歷史最大標記lazyb,下傳時我們用lazylazylazy更新lazyblazyblazyb,再用w+lazybw+lazybw+lazyb更新bbb就好了。

    那么同樣的我們在上面套一個類似吉司機線段樹的東西,維護兩種標記:上次更新后最大值的最大減少值lazyxb上次更新后非最大值的最大減少值lazyb

    然后用類似的方法維護就好了。

    寫起來超級麻煩

    時間復(fù)雜度: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 */

    總結(jié)

    以上是生活随笔為你收集整理的P6242-[模板]线段树3【吉司机线段树】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。