在學習區間更新線段樹之間要先學習單點更新線段樹。
情景引入:
給乙個長度為n的陣列a,隨機修改區間陣列元素 a[l]~a[r] 的值,然後求任意區間和。修改操作我們可以想到:
for
(int i=l; i<=r; i++
)update()
;
但是,這種方法很顯然比較費時,不夠優,所以有了今天要講的區間更新。
懶惰標記:
在講之前要先引入乙個陣列,lazy[ ],正如lazy的名字懶惰一樣,lazy的作用就是偷懶,作為乙個延時標記。
如果要對區間 [ l, r ]的每個元素都加上元素x,與單點更新方式相同,從根結點開始向下尋找合適的節點。如果找到乙個節點的區間完全屬於[ l, r ], 就不用繼續向下修改子節點,而是在這個節點上進行lazy標記。
只看文字敘述難以理解,下面看**有助於理解。
懶惰標記**:
此時發現這個結點[ 6, 10 ] 整好在要求的範圍內,不需要向下找。只需要給[6,10] 這個結點懶惰標記lazy+2,[6,10] 這個結點 leaf= 40+ (10-6+1)*2=50。
這個是一種比較簡單的情況。下面給一種複雜的情況。
來自部落格。
對區間 [ 3, 9 ] 進行修改,如果沒有懶惰標記的幫助,需要對圖中所有綠色
的部分進行修改,所有與[ 3, 9 ] 有交集的集合都要進行修改。需要操作很多次。
如果有lazy標記的幫助,會省掉一些操作。只需要對圖中有橙色標記的部分修改。
懶惰標記在查詢中的作用:
接上圖,更新後,要查詢 [8,10] 之間的元素之和。[8,10] 剛好包含在 [6,10] 中,此時要進行區間更新非常重要的一步操作向下傳遞懶惰標pushdown。
傳遞後的結果如圖所示,然後再往下尋找答案,考慮左孩子,[6, 8]與[ 8,10]有部分重疊,說明這個節點的子樹裡存在某個節點對答案有貢獻,所以繼續向下傳遞懶惰標記。
左節點表示的線段[6,7]與查詢區間無交點,捨去。發現右節點完全包含於[8,10],所以返回[8,8]這個節點的值10, 這樣[6,10]節點的左子樹查詢完畢。
看右節點,發現右節點[9,10]完全包含於[8,10],所以此時不需要向下傳遞,直接返回sum值23,查詢完成,答案為10+23=33。
pushdown**:
// 向下更新
// lazy只有在要使用的時候才會向下傳遞,不用的時候只會標記。
void
pushdown
(int l,
int r,
int k)
}
更新**:void
update
(int ql,
int qr,
int val,
int l,
int r,
int k)
pushdown
(l,r,k)
;int mid=
(l+r)/2
;// ql<=mid, 左子樹和更新區間交集非空
// qr>=mid , 右子樹和更新區間交集非空
if(ql<=mid)
update
(ql,qr,val,l,mid,
2*k);if
(qr>mid)
update
(ql,qr,val,mid+
1,r,
2*k+1)
;pushup
(k);
}
區間更新線段樹完整**:int leaf[max*4]
,lazy[
4*max]
, num[max]
;void
pushup
(int k)
void
pushdown
(int l,
int r,
int k)
}void
buildtree
(int l,
int r,
int k)
int mid=
(l+r)/2
;buildtree
(l,mid,
2*k)
;buildtree
(mid+
1,r,
2*k+1)
;pushup
(k);
}void
update
(int ql,
int qr,
int val,
int l,
int r,
int k)
pushdown
(l,r,k)
;int mid=
(l+r)/2
;if(ql<=mid)
update
(ql,qr,val,l,mid,
2*k);if
(qr>mid)
update
(ql,qr,val,mid+
1,r,
2*k+1)
;pushup
(k);
}int
query
(int ql,
int qr,
int l,
int r,
int k)
區間更新,線段樹
對 l,r 進行區間更新 1 如果結點的區間被查詢區間 l,r 覆蓋,僅對該結點進行更新,並做懶標記,表示該結點被更新過,對該結點的子結點不再進行更新 2 判斷在左子樹查詢,右子樹查詢。查詢過程中,若當前節點帶有懶標記,懶標記下傳給子結點 當前結點懶標記清除,子結點更新並做懶標記 繼續查詢 3 更新...
樹的區間查詢與更新(線段樹)
原題 include include include include include include include include using namespace std define clr x memset x,0,sizeof x define ll long long define typ...
線段樹之區間更新
為什麼這樣?答案是顯然的。線段樹的查詢和單點更新的時間複雜度是o logn 的,如果我每次都只是更新乙個節點,再去詢問,那麼複雜度是o nlogn 這個複雜度是可以接受的,但是如果每次操作我都是更新乙個區間,還是用單點更新去做,那麼複雜度是o n 2logn 這個複雜度是很大的,一般來說,在acm競...