先給出乙個很裸的線段樹板子題:開始給你n個數,標號依次為a[1],a[2]…a[n],接下來有m次操作。每次讓你執行兩個操作:1.將區間[a,b]內的所有數+k。2.查詢[a,b]範圍內所有數的總和。
遇到這種題,我們就可以用線段樹解決。(至於暴力會超時,我之前寫的樹狀陣列部落格中已經提到。)至於線段樹比較快的原因,我就不多提了 (很多部落格都有提及) ,總之它的區間修改以及區間查詢都是log級別的時間複雜度。圖1-1
1.線段樹結點標號(如下圖1-2):從上至下,從左至右依次標號,例如上圖中,區間[1,8]標號為1,區間[1,4]標號為2,區間[5,8]標號為3…顯然,若當前結點下標為
k
,那麼該結點左兒子的下標為2*k
,右兒子的下標為2*k+1
2.父親結點與兒子節點的表示範圍關係:若父親結點表示範圍為[l,r],設mid=(l+r)/2,那麼左兒子的表示範圍為[l,mid]、右兒子的表示範圍為[mid+1,r]。
3.線段樹最下面一層儲存的是原陣列的資訊。
4.線段樹陣列一般開原陣列的四倍大小即夠用。
圖1-2
首先開乙個結構體,用來儲存線段樹中每個結點的資訊(包含的資料範圍,對應的值,包括下文要提到的懶標等)然後我們還需要乙個更新函式,例如我們更改了圖1-2中的結點6,那麼在他之上的結點3和結點1也肯定要更改,這是必不可少的,又因為父親結點的值=左兒子的值+右兒子的值,所以這裡的更新函式就出來了#define max 100010
//資料範圍
int a[max]
;//原陣列
struct node
tr[max *4]
;//線段樹陣列
要使用線段樹肯定得先建好線段樹,這裡我們用遞迴修建線段樹void
update
(int k)
要修改乙個區間,首先得找到乙個區間。那麼如何找乙個區間呢?我們採取的方法是每次都從線段樹頂開始找,也就是編號為1的結點,因為他肯定包含了所有區間情況。假設要修改的區間是[l,r],當前節點k代表的區間範圍是[l,r],mid=(l+r)/2。每一次我們遍歷乙個節點的時候,將[l,r]和[l,r]比較,只會有三種情況:void
build
(int k,
int l,
int r)
//當前節點編號為k,他表示的區間[l,r]的和
int mid =
(tr[k]
.l + tr[k]
.r)>>1;
//將父親區間分割成左區間和右區間
build
(k *
2, l, mid)
;//遞迴建左兒子和右兒子
build
(k *2+
1, mid +
1, r)
;update
(k);
//遞迴到底層之後即可往上回溯修建各個父親結點
}
1.區間[l,r]完全在節點k的左兒子包含範圍內,也就是r<=mid,這時,我們只需往左遞迴即可。
2.區間[l,r]完全在節點k的右兒子包含範圍內,也就是l>mid,同理,我們只需往右遞迴即可。
3.最後一種情況是難點,即l<=mid這樣,如果我們要修改圖1-1中的區間[5,7],那麼我們最終可以找到圖1-2中編號為6和14的結點。修改這兩個區間就可以完成區間[5,7]的修改,問題來了,在節點6之下還有節點12,13,這兩個肯定也要修改。但是如果每次區間修改都改到最底下一層的話,這樣的區間修改複雜度比直接在原陣列上修改還要高。如何解決呢?我們修改了節點6之後先不著急修改下面的結點12,13,(因為我們暫時用不到,沒必要修改)而是在節點6上做乙個標記,表示這下面需要修改,但是我們還沒做!等下次要用到結點12,13了再修改
void
change
(int k,
int l,
int r,
int y)
//當前所在結點為k,我們要將區間[l,r]的值加上y
pushdown
(k);
//下傳懶標,保證所有遍歷到的結點都是更新過的值
int mid =
(tr[k]
.l + tr[k]
.r)>>1;
if(r <= mid)
change
(k *
2, l, r, y)
;else
if(l > mid)
change
(k *2+
1, l, r, y)
;else
update
(k);
//記得更新
}
區間修改的思路理解了,區間查詢也很容易理解void
pushdown
(int k)
//下傳懶標
}
以上就是線段樹的模板,要想真正理解應該也要花點時間,這樣做一些拓展題的時候就可以比較靈活的應用~int
query
(int k,
int l,
int r)
//查詢區間[l,r]
線段樹模板(模板)
參考部落格 持續更新。外鏈轉存失敗,源站可能有防盜煉機制,建議將儲存下來直接上傳 img xhrgdjcd 1613976863463 區間儲存在陣列中的下標對應為 12 3 4 5 6 7 8 9 10 11 12 13 14 15 四部分單點更新 根據題目的要求編寫自己的pushup,query...
線段樹模板
include include include using namespace std const int size 10010 struct node the node of line tree class linetree void updatem void updateline public ...
線段樹模板
單點更新,區間求最值 include include include include include define n 222222 using namespace std int num n struct tree tree n 4 void push up int root void build...