1. 程式人生 > >[學習筆記]Segment Tree Beats!九老師線段樹 【bzoj4695】最假女選手

[學習筆記]Segment Tree Beats!九老師線段樹 【bzoj4695】最假女選手

對於這樣一類問題:

區間取min,區間求和。

N<=100000

要求O(nlogn)級別的演算法

 

直觀體會一下,區間取min,還要維護區間和

增加的長度很不好求。。。。

 

然鵝,

從前有一個來自杭州天水幼兒園的julao叫九條可憐

他發明了一個線段樹的寫法,

攻克了這個難題。

 

說起來很簡單:

線段樹維護區間最大值,區間嚴格次大值,和區間最大值出現次數

修改的時候,如果c大於mx,直接return

如果c小於mx而大於cmx,根據最大值的出現次數可以直接修改sum(注意必須是嚴格大於cmx,否則不能維護好嚴格次大值

如果c小於等於cmx,那麼暴力遞迴左右兒子,最終會用前兩個更新,回溯來pushup一下

複雜度?

前兩個O(1)就回溯了,不管。

第三個操作貌似有些暴力?

由於只有取max,所以

假如開始有O(N)個不同的值,那麼每進行一次第三次操作,至少mx,和cmx要變得一樣。值域減少1

那麼,第三次操作最多進行O(n)次,每次均攤O(logn)

所以複雜度O(nlogn)

 

例題(以及一些具體操作):

bzoj4695. 最假女選手

【bzoj4695】最假女選手

區間還要加?值域會改變,,,可以證明(就是說我不會證)複雜度是O(nlog^2n)

維護區間最大值,次大值,最大值出現次數,最小值同理。以及區間和,區間加標記

下放:

先下放區間加標記,現在兒子的情況大致和父親一樣了

區別在於,之前區間取min可能把最大值砍掉一些,但是沒有在兒子中更新。

由於僅最大值小了一些,所以如果父親的最大值在兒子的最大值和次大值之間,那麼暴力再讓兒子對父親的最大值取個min(直接返回的,這個也是O(1)的)

第二種情況的更新時候:(其中一些操作可能沒有必要)

可能造成最大值和最小值相同的情況,那麼必然就是全部都相等了。特判一下,把次大值-inf,最大值inf

或者可能只有值域只有兩個,那麼次大值或者次小值也要嘗試更新一下。其他值域的時候不影響。

 

程式碼比較長:

#include<bits/stdc++.h>
#define reg register int
#define
il inline #define mid ((l+r)>>1) #define ls t[x].lson #define rs t[x].rson #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=6e5+5; const int inf=0x3f3f3f3f; int n,m; int a[N]; struct node{ int mx,cmx,tmx; int mi,cmi,tmi; ll sum; ll ad; int lson,rson; }t[2*N]; int tot; void pushup(int x){ t[x].sum=t[ls].sum+t[rs].sum; if(t[ls].mx>t[rs].mx){ t[x].mx=t[ls].mx,t[x].tmx=t[ls].tmx;t[x].cmx=max(t[ls].cmx,t[rs].mx); }else if(t[ls].mx<t[rs].mx){ t[x].mx=t[rs].mx,t[x].tmx=t[rs].tmx;t[x].cmx=max(t[rs].cmx,t[ls].mx); }else{ t[x].mx=t[ls].mx;t[x].tmx=t[ls].tmx+t[rs].tmx;t[x].cmx=max(t[rs].cmx,t[ls].cmx); } if(t[ls].mi<t[rs].mi){ t[x].mi=t[ls].mi,t[x].tmi=t[ls].tmi;t[x].cmi=min(t[ls].cmi,t[rs].mi); }else if(t[ls].mi>t[rs].mi){ t[x].mi=t[rs].mi,t[x].tmi=t[rs].tmi;t[x].cmi=min(t[rs].cmi,t[ls].mi); }else{ t[x].mi=t[ls].mi;t[x].tmi=t[ls].tmi+t[rs].tmi;t[x].cmi=min(t[rs].cmi,t[ls].cmi); } } void addmax(int x,int l,int r,int c){//qu max t[x].sum+=(ll)t[x].tmi*(c-t[x].mi); t[x].mi=c; t[x].mx=max(t[x].mx,c); if(t[x].mi==t[x].mx){ t[x].sum=((ll)r-l+1)*c;t[x].tmi=t[x].tmx=r-l+1;t[x].cmi=inf;t[x].cmx=-inf; }else t[x].cmx=max(t[x].cmx,c); } void addmin(int x,int l,int r,int c){ t[x].sum+=(ll)t[x].tmx*(c-t[x].mx); t[x].mx=c; t[x].mi=min(t[x].mi,c); if(t[x].mi==t[x].mx){ t[x].sum=((ll)r-l+1)*c;t[x].tmi=t[x].tmx=r-l+1;t[x].cmi=inf;t[x].cmx=-inf; }else t[x].cmi=min(t[x].cmi,c); } void build(int x,int l,int r){ if(l==r){ t[x].mx=t[x].mi=a[l]; t[x].cmx=-inf;t[x].cmi=inf; t[x].tmx=t[x].tmi=1; t[x].sum=a[l];return; } ls=++tot;rs=++tot; build(ls,l,mid);build(rs,mid+1,r); pushup(x); } void getsum(int x,int l,int r,int c){ t[x].ad+=c;t[x].sum+=(r-l+1)*c; t[x].mi+=c;t[x].cmi+=c; t[x].mx+=c;t[x].cmx+=c; } void pushdown(int x,int l,int r){ if(t[x].ad){ getsum(ls,l,mid,t[x].ad); getsum(rs,mid+1,r,t[x].ad); t[x].ad=0; } if(t[ls].mx>t[x].mx&&t[ls].cmx<t[x].mx) addmin(ls,l,mid,t[x].mx); if(t[rs].mx>t[x].mx&&t[rs].cmx<t[x].mx) addmin(rs,mid+1,r,t[x].mx); if(t[ls].mi<t[x].mi&&t[ls].cmi>t[x].mi) addmax(ls,l,mid,t[x].mi); if(t[rs].mi<t[x].mi&&t[rs].cmi>t[x].mi) addmax(rs,mid+1,r,t[x].mi); } void chanmx(int x,int l,int r,int L,int R,int c){ //cout<<x<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<" "<<c<<" "<<t[x].cmi<<endl; if(L<=l&&r<=R){ if(t[x].mi>=c) return; if(t[x].cmi>c) { addmax(x,l,r,c);return; } pushdown(x,l,r); chanmx(ls,l,mid,L,R,c); chanmx(rs,mid+1,r,L,R,c); pushup(x); return; } pushdown(x,l,r); if(L<=mid) chanmx(ls,l,mid,L,R,c); if(mid<R) chanmx(rs,mid+1,r,L,R,c); pushup(x); } void chanmi(int x,int l,int r,int L,int R,int c){ if(L<=l&&r<=R){ if(t[x].mx<=c) return; if(t[x].cmx<c) { addmin(x,l,r,c);return; } pushdown(x,l,r); chanmi(ls,l,mid,L,R,c); chanmi(rs,mid+1,r,L,R,c); pushup(x); return; } pushdown(x,l,r); if(L<=mid) chanmi(ls,l,mid,L,R,c); if(mid<R) chanmi(rs,mid+1,r,L,R,c); pushup(x); } void add(int x,int l,int r,int L,int R,int c){ if(L<=l&&r<=R){ getsum(x,l,r,c); return; } pushdown(x,l,r); if(L<=mid) add(ls,l,mid,L,R,c); if(mid<R) add(rs,mid+1,r,L,R,c); pushup(x); } int qmax(int x,int l,int r,int L,int R){ if(L<=l&&r<=R){ return t[x].mx; } pushdown(x,l,r);int ret=-inf; if(L<=mid) ret=max(ret,qmax(ls,l,mid,L,R)); if(mid<R) ret=max(ret,qmax(rs,mid+1,r,L,R)); return ret; } int qmin(int x,int l,int r,int L,int R){ if(L<=l&&r<=R){ return t[x].mi; } pushdown(x,l,r);int ret=inf; if(L<=mid) ret=min(ret,qmin(ls,l,mid,L,R)); if(mid<R) ret=min(ret,qmin(rs,mid+1,r,L,R)); return ret; } ll qsum(int x,int l,int r,int L,int R){ if(L<=l&&r<=R){ return t[x].sum; } pushdown(x,l,r);ll ret=0; if(L<=mid) ret+=qsum(ls,l,mid,L,R); if(mid<R) ret+=qsum(rs,mid+1,r,L,R); return ret; } int main(){ rd(n); for(reg i=1;i<=n;++i){ rd(a[i]); } rd(m); ++tot; build(1,1,n); //cout<<" tot "<<tot<<endl; int op,l,r,x; int o=0; while(m--){ ++o; // cout<<" oooo "<<o<<endl; rd(op); switch(op){ case 1:rd(l);rd(r);rd(x);add(1,1,n,l,r,x);break; case 2:rd(l);rd(r);rd(x);chanmx(1,1,n,l,r,x);break; case 3:rd(l);rd(r);rd(x);chanmi(1,1,n,l,r,x);break; case 4:rd(l);rd(r);printf("%lld\n",qsum(1,1,n,l,r));break; case 5:rd(l);rd(r);printf("%d\n",qmax(1,1,n,l,r));break; case 6:rd(l);rd(r);printf("%d\n",qmin(1,1,n,l,r));break; } } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/12/27 9:57:30 */