線段樹之區間更新線段樹

2021-10-08 09:31:30 字數 3017 閱讀 1747

在學習區間更新線段樹之間要先學習單點更新線段樹。

情景引入:

給乙個長度為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競...