集訓筆記5th 樹狀陣列與線段樹的入門

2021-09-25 20:17:04 字數 3742 閱讀 1799

樹狀陣列(bin

aryi

ndex

edtr

ee

sbinary indexed trees

binary

inde

xedt

rees

)是一種由於維護序列字首和的資料結構。對於給定序列a

aa,我們建立乙個陣列c

cc,其中c[x

]c[x]

c[x]

儲存序列a

aa的區間[x−

lowb

it(x

)+1,

x]

[x - lowbit(x)+1, x]

[x−low

bit(

x)+1

,x]中所有數的和,即∑i=

x−lo

wbit

(x)+

1xa[

i]

\sum^_

∑i=x−l

owbi

t(x)

+1x​

a[i]

。事實上, 陣列c

cc可以看做成乙個樹形結構,該結構滿足以下性質:

每個內部節點c[x

]c[x]

c[x]

儲存以它為根的子樹中所有葉節點的和。

每個內部節點c[x

]c[x]

c[x]

的子節點個數等於low

bit(

x)

lowbit(x)

lowbit

(x)的個數(low

bit(

x)=x

&(−x

)lowbit(x) = x \& (-x)

lowbit

(x)=

x&(−

x))。除了樹根外,每個內部節點c[x

]c[x]

c[x]

的父節點是c[x

+low

bit(

x)

]c[x+lowbit(x)]

c[x+lo

wbit

(x)]

。樹的深度為o(l

ogn)

o(logn)

o(logn

)。樹狀陣列查詢字首和

int

ask(

int x)

樹狀陣列單點增加同時維護字首和
void

add(

int x,

int y)

在執行所有操作之前, 我們需要對樹狀陣列進行初始化——針對原始序列a

aa構造乙個樹狀陣列。

方法是:從小到大一次考慮每個節點x

xx,借助low

bi

tlowbit

lowbit

運算掃瞄它的子節點並求和。時間複雜度為o(n

)o(n)

o(n)

。線段樹(seg

ment

segment

segmenttr

ee

tree

tree

)是一種基於分治思想的二叉樹, 用於在區間上進行資訊統計, 可儲存複雜資訊, 比樹狀陣列更通用, 但是**量較大。

1.線段樹的每個節點都代表著乙個空間。

2.線段樹具有唯一的根節點, 代表的區間是整個統計範圍, 如[1, n]。

3.線段樹的每個葉子節點都代表乙個長度為1的區間[1, x]。

4.對於每個內部節點[l, r],它的左子節點是[l, mid], 右子節點是[mid+1,r],其中mid = (l + r) / 2(向下取整)。

線段樹的向上更新

以求區間最大值為例:

void

pushup

(int p)

以求區間和為例:

void

pushup

(int p)

在精準更新某個特定區間後, 所有以這個區間為子區間的區間都要依次更新。

線段樹的單元

struct node
線段樹的建立

相當於對陣列維護的初始化

void

build

(int p,

int l,

int r)

int mid =

(l + r)

>>1;

build

(p<<

1, l, mid)

;build

(p<<1|

1, mid +

1, r)

;//對對應某區間的節點的兩個子節點更新

pushup

(p);

}

線段樹的區間查詢

較靈活,可以根據題意進行靈活地改動

這裡以求某段區間中的最大值為例(於是用這個模板也可以求出最小值和極差)

int

ask(

int p,

int l,

int r)

線段樹的單點修改
void

change

(int p,

int x,

int v)

int mid =

(t[p]

.l + t[p]

.r)/2;

if(x <= mid)

change

(p<<

1, x, v)

;else

change

(p<<1|

1, x, v)

; t[p]

.val =

max(t[p<<1]

.val, t[p<<1|

1].val)

;}

線段樹的區間修改

為了節省時間, 通常在結構體裡多維護乙個變數lazy標記,這樣可以減少更新子節點的次數。注意有的題目要求要對每個葉子節點更新,就不可以用lazy標記。有的題目裡可能本身的資訊就可以充當lazy標記起到作用,就可以不用多維護變數。

void

pushdown

(int p)

t[p<<1]

.lazy +

= t[p]

.lazy;

t[p<<1|

1].lazy +

= t[p]

.lazy;

t[p<<1]

.lazy =0;

}void

change

(int p,

int l,

int r,

int d)

pushdown

(p);

int mid =

(t[p]

.l + t[p]

.r)>>1;

if(l <= mid)

change

(p<<

1, l, r, d);if

(r > mid)

change

(p<<1|

1, l, r, d)

;pushup

(p);

}int

ask(

int p,

int l,

int r)

樹狀陣列與線段樹

推一下關於樹狀陣列的講解部落格 和線段樹的講解 package test2 public class 線段樹 int len a.length segtree t buildtree 0,len 1,a int sum0 2 query t,0,2 int sum1 3 query t,1,3 查詢...

樹狀陣列與線段樹(三)

找規律題 1.螺旋折線 如下圖所示的螺旋折線經過平面上所有整點恰好一次。對於整點 x,y 我們定義它到原點的距離 dis x,y 是從原點到 x,y 的螺旋折線段的長度。例如 dis 0,1 3,dis 2,1 9 給出整點座標 x,y 你能計算出 dis x,y 嗎?輸入格式 包含兩個整數 x,y...

樹狀陣列與線段樹(二)

樹狀陣列 1.小朋友排隊 n n 個小朋友站成一排。現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。每個小朋友都有乙個不高興的程度。開始的時候,所有小朋友的不高興程度都是 0 0 如果某個小朋友第一次被要求交換,則他的不高興程度增加 1 1 如果第二次要求他交換,則他的不...