線段樹詳解

2021-10-17 08:19:53 字數 2956 閱讀 6203

有這樣一類問題 設有長度為n的數列 需要進行一些操作 比如求區間的最大值 (求某個區間的和 或者在進行某些修改操作後再求區間的和 ) 如果用普通的陣列儲存數列 然後進行暴力求解的話總複雜度是很大的(當數列很長 查詢的次數很多時) 這種方法肯定是不可取的

對於這種問題,有一種神奇的資料結構,能在o(mlog2 n)的時間內解決,這就是線段樹。

那你學習線段樹不得知道怎麼構建乙個線段樹嗎?

差不多就是這樣 如下圖

(畫的有點醜)

將這個線段樹整合放到陣列裡面就是下面這樣的tree陣列 打×的位置是沒有此節點的 線段樹節點旁邊的數字就是在陣列中的下標

很容易發現 當前a節點(node為a節點的下標)的左孩子的下標left=node2+1,

右孩子的下標right=node2+2;

現在我們就有兩個陣列了乙個原始陣列arr,乙個線段樹陣列tree;

void

build

(ll l, ll r, ll node)

int mid =

(l + r)/2

;build

(l, mid, node *2)

;build

(mid +

1, r, node *2+

1); tree[node]

= tree[node *2]

+ tree[node *2+

1];}

其實就是運用遞迴 折半進行構造的

void

update

(ll s, ll t, ll l, ll r, ll node)

else

}

比如現在你就要求arr陣列的arr[0]到arr[4]中所有數的和

一般操作來說 我們需要找到arr陣列中的值在tree陣列中對應的值然後再加起來

但是既然我們已經把線段樹給構造出來了 肯定是不會用這個很費時間的方法了

同理 從tree的根節點開始查詢 然後開始往下查詢 但是呢當你查詢到乙個區間在你所要求和的區間之內的話 就可以直接返回tree這個區間的值 而不用再往下查詢了

例如sum[0-4]=arr[0]+arr[1]+arr[2]+arr[3]+arr[4]=tree[0-2]+tree[3-4];

ll query

(ll l, ll r, ll l, ll r, ll nod)

點修改只能修改線段樹上的某個點。區間修改是很複雜的問題。

這裡就要引入一種「懶惰(lazy)」的做法。當修改的是乙個整塊區間時,只對這個線段區間進行整體上的修改,其內部每個元素的內容先不做修改,只有當這部分線段的一致性被破壞時才把變化值傳遞給子區間,那麼,每次區間修改的複雜度是o(log2 n),總複雜度是o(nlog2 n)。 對於lazy操作的實現,需要對每次操作的子區間記錄狀態;

需要定義乙個lazy陣列儲存修改

因為這個lazy陣列很懶 所以只有用到它時才會往下更新

void

pushdown

(ll nod, ll l, ll r)

}void

update

(ll l,ll r,ll val,ll l,ll r,ll nod)

else

}ll query

(ll l, ll r, ll l, ll r, ll nod)

但是要注意lazy陣列裡面儲存的是變化的值 而tree是實際變化的 所以當區間被修改的時候

區間的和(也就是線段樹的節點也要被修改)

與單點修改不同的是 引入了lazy陣列記錄區間修改的值

當然這裡的修改只涉及到加減操作 如果涉及到乘除的話(以後再補充吧?)

說了這麼多舉個例子看看吧

p3372 【模板】線段樹 1

其實這就是一道模板題

只要你熟悉了線段樹 知道了lazy的使用 直接碼就好了

注意資料的範圍有點大 要用long long;

給個模板吧

#include

#include

#include

using

namespace std;

typedef

long

long ll;

const

int maxt =

100100

;ll h[maxt]

, tree[maxt *4]

, lazy[maxt *4]

;void

pushdown

(ll nod, ll l, ll r)

}void

build

(ll l, ll r, ll nod)

int mid =

(l + r)/2

;build

(l, mid, nod *2)

;build

(mid +

1, r, nod *2+

1); tree[nod]

= tree[nod *2]

+ tree[nod *2+

1];}

void

update1

(ll s, ll t, ll l, ll r, ll nod)

else

}void

update

(ll l,ll r,ll val,ll l,ll r,ll nod)

else

}ll query

(ll l, ll r, ll l, ll r, ll nod)

intmain()

else

}return0;

}

本人小白 如有不正確的地方 歡迎指正

//南昌理工acm集訓隊

線段樹 詳解

acm刷題時遇到許多連續區間的動態查詢問題,例如求取某一區間上元素之和 求取某一區間上元素的最大值,此時如果使用一般的方法求解會使得時間超出要求。此時需要使用到線段樹,其主要用於高效解決連續區間的動態查詢問題。線段樹,類似區間樹,是乙個完全二叉樹,它在各個節點儲存一條線段 陣列中的一段子陣列 由於二...

線段樹詳解

線段樹是處理區間問題的好的解決方法,當有n個元素時對區間的操作可以在o logn 時間內完成,有q個詢問也不會超時,根據節點維護的資料的不同,線段樹可以提供不同的功能,下面以rang minimum query rmq,即查詢區間內最小值 為例,進行說明。對於陣列,線段樹結構為 其維護區間與儲存下標...

線段樹詳解

一 定義 首先要明確線段樹的定義,線段樹是一顆樹,而且是完全二叉樹。同時線段樹的每個節點表示乙個區間,左子樹和右子樹分別表示這個區間的左半邊和右半邊。即將區間 l,r 分解成 l,mid 和 mid 1,r 假設根的高度為1,樹高為 n 1 下圖展示了區間 1,13 的分解過程 二 原理 上圖中每個...