Segment Tree Beats 區間最值問題
線段樹一類特殊技巧!
引出:CF671C Ultimate Weirdness of an Array
其實是考試題,改題的時候並不會區間取最值,區間求和,之后秉承着好好學習的態度,學習了Segment tree Beats
套路是維護出區間最小值和次小值,以及區間最小值數量。之后再維護出題目中需要的東西就好了。之后怎么處理呢,如果我們需要維護出區間和x取max,那么,如果x<=minn[rt],那么直接return;如果x<minx[rt](minx[rt]為區間次小值),那么之間將最小值改成x,之后再改需要改的信息就好了...但是,如果x>=minx[rt],那么就遞歸處理子樹信息。
什么?你說它的時間復雜度?每次修改均攤O(nlogn)
什么時候修改是O(n)呢,就是所有點的狀態大概接近於1,2,1,2,1,2,1,2,1,2...之后對3取max,這種情況下單次修改是O(n)的,但是修改完這次之后,不論下一次修改是什么,都是O(1)的,並且,求和不會超過O(nlogn)
其實本質上,什么拖累了時間復雜度呢?就是當區間中不同的元素特別多的時候,單次修改的時間復雜度會較為高昂,但是假設,我們修改了A個元素,那么就相當於區間的集合中少了A-1個元素,並且我們知道這次修改最大是Alogn的,那么下一次修改就相當於少了A-1個不同元素,也就是說,假設沒有這次修改,下一次修改B個元素的話,那么有了這次修改下一次就相當於修改了B-A+1個元素,那么我們考慮將所有的修改操作的時間復雜度求和,接近於O(nlogn),但是,這並不完全,因為每次修改可能會導致某些區間的元素個數增加。那么我們考慮會增加多少呢?因為所有修改的元素都修改成了一個值,那么就相當於有些區間的元素的個數多了1。那么修改的時間復雜度就是期望上(Qlogn+nlogn),但是其實並不會跑滿,而且實際效率很快。
那么線段樹的題嘛,考慮加上區間修改等操作的時間復雜度。因為每次區間覆蓋的話,非常棒,取消了許多節點的獨立性,時間復雜度完全沒有問題!但是區間加的話假如一個區間的是1,2,3,4,5,6,1,2,3,4,5,6之后針對前6個區間加6,那么相當於增加了一倍的獨立點個數,但是反過來考慮,構成1,2,3,4,5,6,1,2,3,4,5,6需要這種特殊構造,而特殊構造耗費的操作數量是接近n級別的,那么其實反過來考慮一下,時間復雜度沒有任何問題,因為,其實我們考慮,只有區間修改的時間復雜度,是logn,那么為什么是O(logn)的原因是因為打了lazy標記,那么其實我們假裝這里打了一個lazy標記,那么其實每次修改的節點只有log級別的,那么其實A也只增加了log級別的存在,而每次PushDown增加的也只是logn*Alogn其實也就是Alognlogn的時間復雜度。這非常正確,請不要反駁我。
那么我們考慮完時間復雜度了,是不是該講題了?
並不是,因為例題沒有那么多...
那么我們先舉一個例子
給定一個長度為n的數列A,接下來有m次操作:
• 區間[l,r]中的所有數變成min(Ai,x)
• 詢問區間[l,r]中所有數的和
• n,m≤50000 分塊!(抱歉,我不會!)
• n,m ≤ 500000
裸題,按照上述要求維護一下就可以了,如果想寫的話,可以聯系我,我這里有我自己出的數據...
給定一個長度為n的數列A,接下來有m次操作:
• 區間[l,r]中的所有數變成min(Ai,x)
• 區間[l,r]中的所有數加上x(x可能是負數)
• 詢問區間[l,r]中所有數的和
• n,m≤50000 分塊!
• n,m ≤ 500000
同樣是裸題,多了一個操作,HDU上有...題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5306
剩下的就是例題時間了!
BZOJ4355: Play with sequence
這個是不是有一點感覺了呢?
分析:其實還是區間操作裸題,我們對於操作1,之間區間覆蓋,對於操作2先區間加,再區間取max...其實並不是很難對不對?對於操作3,之間輸出1-n的最小值個數(如果最小值是0的話)
附上代碼:
#include <queue> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define N 300505 #define ls rt<<1 #define rs rt<<1|1 #define ll long long #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 ll minn[N<<2],minx[N<<2],cn[N<<2],cov[N<<2],add[N<<2];int n,Q; void PushUp(int rt) { if(minn[ls]<minn[rs])minn[rt]=minn[ls],minx[rt]=min(minn[rs],minx[ls]),cn[rt]=cn[ls]; else if(minn[ls]==minn[rs])minn[rt]=minn[ls],minx[rt]=min(minx[rs],minx[ls]),cn[rt]=cn[ls]+cn[rs]; else minn[rt]=minn[rs],minx[rt]=min(minn[ls],minx[rs]),cn[rt]=cn[rs]; } void PushDown(int m,int rt) { if(cov[rt]!=-1)minn[rs]=minn[ls]=cov[ls]=cov[rs]=cov[rt],cn[ls]=(m-(m>>1)),cn[rs]=(m>>1),minx[ls]=minx[rs]=1ll<<62,add[ls]=add[rs]=0,cov[rt]=-1; if(add[rt])minn[ls]+=add[rt],minn[rs]+=add[rt],minx[ls]+=add[rt],minx[rs]+=add[rt],add[ls]+=add[rt],add[rs]+=add[rt],add[rt]=0; if(minn[rt]>minn[ls])minn[ls]=minn[rt];if(minn[rt]>minn[rs])minn[rs]=minn[rt]; } void build(int l,int r,int rt) { cov[rt]=-1,add[rt]=0; if(l==r){scanf("%lld",&minn[rt]),minx[rt]=1ll<<62,cn[rt]=1;return;} int m=(l+r)>>1;build(lson);build(rson);PushUp(rt); } void Update_cov(int L,int R,ll c,int l,int r,int rt) { if(L<=l&&r<=R){minn[rt]=cov[rt]=c,minx[rt]=1ll<<62,cn[rt]=r-l+1,add[rt]=0;return;}PushDown(r-l+1,rt);int m=(l+r)>>1; if(L<=m)Update_cov(L,R,c,lson);if(m<R)Update_cov(L,R,c,rson);PushUp(rt); } void Update_add(int L,int R,ll c,int l,int r,int rt) { if(L<=l&&r<=R){minn[rt]+=c,add[rt]+=c,minx[rt]+=c;return;}PushDown(r-l+1,rt);int m=(l+r)>>1; if(L<=m)Update_add(L,R,c,lson);if(m<R)Update_add(L,R,c,rson);PushUp(rt); } void Update_max(int L,int R,ll c,int l,int r,int rt) { if(c<=minn[rt])return ; if(L<=l&&r<=R&&minx[rt]>c){minn[rt]=c;return ;}PushDown(r-l+1,rt);int m=(l+r)>>1; if(L<=m)Update_max(L,R,c,lson);if(m<R)Update_max(L,R,c,rson);PushUp(rt); } ll query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R)return minn[rt]?0:cn[rt];PushDown(r-l+1,rt);int m=(l+r)>>1;ll ret=0; if(L<=m)ret+=query(L,R,lson);if(m<R)ret+=query(L,R,rson);return ret; } int main() { scanf("%d%d",&n,&Q); build(1,n,1); while(Q--) { int x,y,op;ll z;scanf("%d%d%d",&op,&x,&y); if(op==1)scanf("%lld",&z),Update_cov(x,y,z,1,n,1); else if(op==2)scanf("%lld",&z),Update_add(x,y,z,1,n,1),Update_max(x,y,0ll,1,n,1); else printf("%lld\n",query(x,y,1,n,1)); } return 0; }
好吧好吧...其實例題目前只有一道...目測還有最假女選手什么的...好麻煩的說...