線段樹(segmenttree)是一種基於分治思想的二叉樹結構,用於區間上進行資訊統計。與按照二進位制位進行劃分的樹狀陣列相比,線段樹是一種更加通用的結構。
性質
線段樹每個節點都代表乙個區間。
線段樹具有唯一的根節點,代表的區間是整個統計範圍。
線段樹的每個葉節點都代表乙個長度為1的元區間.
對於每個內部節點[l, r],它的左子節點是[l, mid],右子節點是[mid+1, r],其中mid = (l + r) >> 1。
建樹
每個葉節點t[i, i]維護a[i]的值,從而使資訊從下向上傳遞資訊。
單點更改
從根節點出發,找到區間[x, x]的葉節點,然後從下向上更新。
區間查詢
找到完全覆蓋當前節點的區間,立即回溯。
若左子節點有重合的部分,則訪問左子節點。
若右子節點有重合的部分,則訪問右子節點。
延遲標記
區間修改的指令中,某個區間的結點改變,整棵子樹中的所有節點儲存的資訊都會發生變化,修改的時間複雜度將會增加到o(n)。
我們發現,若我們將區間的整棵子樹進行更新,卻始終沒有進行查詢,那麼整棵子樹的更新都是徒勞的。所以,我們可以在修改指令時找到完全覆蓋的區間後立即返回,只是在其中增加乙個標記,表示此節點已經更改,但子樹都未更新。
此後的更改和查詢命令中,在向下訪問之前,我們先將未更新的結點向下傳遞,然後清除當前結點的標記。這樣一來,時間複雜度仍可維持在o(logn)。
模板:
如題,已知乙個數列,你需要進行下面兩種操作:
1.將某區間每乙個數加上x
2.求出某區間每乙個數的和
輸入格式:
第一行包含兩個整數n、m,分別表示該數列數字的個數和操作的總個數。
第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來m行每行包含3或4個整數,表示乙個操作,具體如下:
操作1: 格式:1 x y k 含義:將區間[x,y]內每個數加上k
操作2: 格式:2 x y 含義:輸出區間[x,y]內每個數的和
輸出格式:
輸出包含若干行整數,即為所有操作2的結果。
1 #include2using
namespace
std;
3const
int size = 100005;4
struct
segmenttreet[size*4];8
inta[size], n, m;910
void build(int p, int l, int
r)16
int mid = (l + r) >> 1
;17 build(p*2
, l, mid);
18 build(p*2+1, mid+1
, r);
19 t[p].sum = t[p*2].sum + t[p*2+1
].sum;20}
2122
void spread(int
p)30}31
32void change(int p, int l, int r, int
d)38
spread(p);
3940
int mid = (t[p].l + t[p].r) >> 1;41
if(l <= mid) change(p*2
, l, r, d);
42if(r > mid) change(p*2+1
, l, r, d);
43 t[p].sum = t[p*2].sum + t[p*2+1
].sum;
44}
4546
long
long ask(int p, int l, int
r)55
56int
main() 72}
73return0;
74 }
延遲標記 線段樹
延遲標記 例題以poj3468為例,我們使用線段樹 延遲標記技巧 來實現快速區間修改與區間查詢。具體做法是,我們為每個節點增加乙個延遲標記add,如果add為0,則說明該點的所有子區間都已更新完成,否則說明其子區間仍需要 add。請注意,如果乙個節點被打上 延遲標記 說明該節點曾經被修改過,但其子節...
延遲標記 線段樹
以poj3468為例,我們使用線段樹 延遲標記技巧 來實現快速區間修改與區間查詢。具體做法是,我們為每個節點增加乙個延遲標記add,如果add為0,則說明該點的所有子區間都已更新完成,否則說明其子區間仍需要 add。請注意,如果乙個節點被打上 延遲標記 說明該節點曾經被修改過,但其子節點尚未被更新,...
線段樹模板 (poj 3468)延遲標記
參考了胡浩大牛的 風格。include define lson l,m,rt 1 define rson m 1,r,rt 1 1 const int maxn 100010 long long add maxn 4 1 long long sum maxn 4 1 void pushup int ...