線段樹詳解

2022-07-05 16:54:13 字數 1968 閱讀 6780

一:定義

首先要明確線段樹的定義,線段樹是一顆樹,而且是完全二叉樹。同時線段樹的每個節點表示乙個區間,左子樹和右子樹分別表示這個區間的左半邊和右半邊。

即將區間[l,r]分解成[l,mid]和[mid+1,r],假設根的高度為1,樹高為

(n>1)

下圖展示了區間[1,13]的分解過程

二:原理

上圖中每個節點儲存自己對應區間的資訊。

(1)單點修改

假設要修改1號節點,不難發現只要修改[1,13]、[1,7]、[1,4]、[1,2]、[1,1]這幾個節點的資訊,也就是更新1號節點到根節點的這條鏈上的所有資訊

所以最大修改次數就是樹的高度

(2)區間查詢

我們可以將乙個[1,n]的線段樹分成若干個連續不相交區間,嗯大概是

詳細證明可以參考

(3)區間修改

樸素演算法:把區間內的每個點單點修改(好像有那麼一點點...暴力)

那該怎麼辦呢,這時候乙個神奇的東西出現了——懶惰標記(lazy tag)

我們把對節點的修改情況儲存在標記裡,在訪問乙個節點的時候,「順便」把它的標記傳遞給它的兒子節點,也就是懶惰標記的下放。

實現思路(重點)

<1>增加乙個新的變數用來儲存lazy tag

<2>遞迴到這個節點時,只更新這個節點的狀態,並把當前的更改值累積到標記裡

<3>當需要遞迴這個節點的子節點的時候,標記下傳給子節點

①當前節點的懶惰標記累積到子節點的懶惰標記中

②修改子節點狀態(原狀態+子節點區間大小*父節點傳下來的懶惰標記)

③父節點懶惰標記清零

三:**實現

const int maxn=50010;

int a[maxn],ans[maxn<<2],lazy[maxn<<2];

//a為原序列資訊,ans模擬線段樹維護區間和,lazy為懶惰標記

void pushup(int rt)

void build(int l,int r,int rt)

int mid=(l+r)>>1;

build(l,mid,rt<<1);

build(mid+1,r,rt<<1|1);

pushup(rt);

}void pushdown(int rt,int ln,int rn)//ln表示左子樹元素結點個數,rn表示右子樹結點個數

}void add(int l,int c,int l,int r,int rt)

int mid=(l+r)>>1;

//pushdown(rt,mid-l+1,r-mid); 若既有點更新又有區間更新,需要這句話

if (l<=mid)

add(l,c,l,mid,rt<<1);

else

add(l,c,mid+1,r,rt<<1|1);

pushup(rt);

}void update(int l,int r,int c,int l,int r,int rt)

int mid=(l+r)>>1;

pushdown(rt,mid-l+1,r-mid);

if (l<=mid) update(l,r,c,l,mid,rt<<1);

if (r>mid) update(l,r,c,mid+1,r,rt<<1|1);

pushup(rt);

}ll query(int l,int r,int l,int r,int rt)

int main()

線段樹 詳解

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

線段樹詳解

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

線段樹詳解

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