有這樣一類問題 設有長度為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 的分解過程 二 原理 上圖中每個...