P6242-[模板]线段树3【吉司机线段树】
正題
題目鏈接:https://www.luogu.com.cn/problem/P6242
題目大意
給出一個長度為nnn的序列aaa,mmm次要求支持操作
1≤n,q≤5×1051\leq n,q\leq 5\times 10^51≤n,q≤5×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 mxk≥mx,此時不會對區(qū)間產(chǎn)生影響,勢能不變。
- mx>k>semx>k>semx>k>se,此時我們暴力修改mxmxmx和ttt,并以此修改sumsumsum,勢能不變。
- k≥sek\geq sek≥se,此時我們暴力遞歸左右兩邊修改,此時我們相當于多做了一次操作,但是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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我的世界怎么种树
- 下一篇: AT3913-XOR Tree【状压dp