演算法學習筆記 線段樹

2022-05-06 21:48:11 字數 2897 閱讀 2116

在樹狀陣列那篇部落格中,留下了乙個坑:

區間修改區間查詢

樹狀陣列部落格傳送門

今天我們就要來解決這個問題

都很簡單

線段樹是一種可以較快維護滿足區間可加性區間資訊(如:區間和,區間積,區間最大最小等)的資料結構,其基本思想就是二分。

注:區間可加性指一些可以通過子區間資訊合併維護的資訊,如區間最大就可以由二分後兩個子區間最大值得到

以下**運用到的巨集和全域性變數及其解釋:

#define ls now<<1

#define rs now<<1|1

對於一棵線段樹,我們可以遞迴地建樹。根據二分的思想,我們可以每次進行二分,分別建立左子樹和右子樹,直到遞迴到葉子結點(即\(l=r\))

pushup操作是用來合併子區間資訊的操作,對於區間加和,此操作所幹的事就是\(sumv_=sumv_+sumv_\)

建樹**如下:

inline void pushup(int now)

inline void build(int now,int l,int r)

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

build(ls,l,mid);

build(rs,mid+1,r);

pushup(now);

}

其實完全不用這樣維護

基本思想:二分查詢

**:

inline void change(int now,int l,int r,int x,int v)

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

if(x<=mid) change(ls,l,mid,x,v);

else change(rs,mid+1,r,x,v);

pushup(now);

}

思想:對於乙個想要修改的區間\([ql,qr]\),假設你現在到了編號為\(now\)的節點,其管控的區間為\([l,r]\),中點為\(mid\),如果\(ql \leq mid\)則證明修改區間與\([l,mid]\)有相交的部分,需修改左半區間;同理,如果\(mid,則需修改右區間(如下圖)

**如下:

inline void change(int now,int l,int r,int ql,int qr,int v)

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

if(ql<=mid) change(ls,l,mid,ql,qr,v);

if(mid思想:對於乙個想要修改的區間\([ql,qr]\),假設你現在到了編號為\(now\)的節點,其管控的區間為\([l,r]\),中點為\(mid\),如果\(ql>mid\),則證明所需修改的區間全部位於右半區間及其以右,修改右區間;同理,如果\(qr\leq mid\),需修改右區間。額外的,其他情況則證明修改區間左右兼佔,需修改兩個區間(如下圖)

inline void optadd(int now,int l,int r,int ql,int qr,int v)

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

if(ql>mid) optadd(rs,mid+1,r,ql,qr,v);

else if(qr<=mid) optadd(ls,l,mid,ql,qr,v);

else optadd(ls,l,mid,ql,qr,v),optadd(rs,mid+1,r,ql,qr,v);

}

注:請注意二行版與三行版關於情況之間關係的不同

思想:對於乙個查詢區間\([ql,qr]\),為了節省時間,只需遞迴到乙個包含於此區間的區間\([l_i,r_i]\)即可。\(ans=\sum[l_i,r_i]\)

注:二行版和三行版思想與上方相同,下同,不再解釋

//二行版

inline int query(int now,int l,int r,int ql,int qr)

可以發現,我們在區間修改這一環節時間開銷比較大,每次修改都需要遞迴到葉子結點才可以完成。但是查詢時並不需要查詢每個葉子節點的值

所以懶惰的人類就有了\(lazy\)標記這種做法

我們只需要在每次修改時為區間打上乙個標記,在需要查詢葉子結點時再將標記和修改下放一層,就可以以較小的時間開銷完成修改。

\(pushdown\)操作就是完成上述下放標記的乙個操作。首先,很顯然的一點是如果當前節點不存在任何標記,我們就不需要對其下放。

**如下

inline void pushdown(int o,int l,int r)
注:下放標記後必須清零,因為如果不清零,當前區間就相當於多加了乙個標記。

優化後的查詢(\(query\))操作只需在原有**遞迴前進行標記下方即可

優化後的修改操作(\(change\))基本形式和查詢十分相似,具體見**

\(poj2182\)

思路:權值線段樹+樹上二分查詢區間第k大

$ sp2713\ or\ p1415 $ 線段樹區間每個數開方+區間和

思路:標記活用或同時維護區間和和區間最大+單點修改\最大樹與求和樹同時維護

\(fbi樹\)

思路:二叉樹經典例題,也可以用線段樹解決

演算法學習筆記 樹狀陣列 線段樹

支援單點修改和區間查詢兩個操作,單次操作o logn tr x 陣列存的是原陣列在區間 x lowbit x x 上的和 若要將乙個數x變為v,則將x x v,即加上v x 模板如下 建立樹狀陣列 for int i 1 i n i scanf d a i for int i 1 i n i add...

演算法學習14 線段樹

線段樹要解決的問題是 在乙個實時更新的動態陣列中查詢區間和 或廣義的區間狀態,如區間積,區間最大最小值 若所查詢的陣列為靜態陣列,則此問題只需要使用陣列字首和即能解決.線段樹是一種平衡二叉樹,其每個節點儲存陣列中一段區間和.其左子節點儲存區間左半部分和 其右子節點儲存區間右半部分和.線段樹陣列的內容...

演算法學習筆記 主席樹

出題出掛了,來好好學主席樹了 線段樹沒了 對於使用線段樹,我們可以較好地解決 帶修改的全域性第k大 或小 問題 但是對於某個區間進行求第k大 或小 操作就不是那麼容易了。可持久化一詞在資料結構中十分常見。可持久化 的意思就是 帶有歷史版本的 資料結構。而在我們所接觸到的基本資料結構中 如陣列 並查集...