模板:线段树
文章目錄
- 引言
- 思想
- 模板
- 建樹
- 單點修改 / 查詢
- 區間修改/查詢
- 總結
- 練習
引言
有一類題目:要求在區間上維護信息,比如帶修改區間求和問題。考慮到枚舉求和肯定會超時,我們可以通過一些數據結構來維護信息,例如線段樹。
它功能強大,支持區間求和,區間求最值、區間修改等一系列操作。
思想
線段樹,是一種二叉搜索樹(即每個結點最多有兩棵子樹,通常叫做左右子樹)。
它將一段區間劃分為若干單位區間,每一個節點都儲存著一段區間[L,R]的信息,其中葉子節點L=R。它的大致思想是:將區間[1,n]平均分成2個小區間,再將小區間平均分成2個更小區間…以此類推,直到區間長度變為1無法分成兩個更小的區間。通過對這些小區間進行修改、查詢,來實現對大區間的修改、查詢。
用線段樹維護的問題必須滿足可以用兩個小區間的信息合并成大區間信息,否則是不可能將大問題劃分成子問題來解決的。
下圖就是一棵[1,10]的線段樹的分解過程。(相同顏色的節點在同一層)
可以證明,線段樹的最大深度不超過log2n
模板
整體來說都是在貫徹遞歸二分分治的思想。注意k結點的兩個孩子編號應分別為2k 和 2k+1
注意:關于線段樹的數組要開到4n(最差時)!
建樹
思想:常見的做法是遍歷整棵線段樹,設當前遍歷到的節點所代表的區間為[l,r],設中點mid=(l+r)/2,則左子樹區間[l,mid],右子樹的區間為[mid+1,r],注意要遞歸到線段樹的葉節點才結束,并給每一個節點賦值,葉子節點直接賦值,非葉子節點信息由左右子樹信息合并而來。
void build(int k,int l,int r){if(l==r){sum[k]=f[l];return;}int mid=(l+r)>>1;build(2*k,l,mid);build(2*k+1,mid+1,r);sum[k]=sum[2*k]+sum[2*k+1]; }單點修改 / 查詢
用樹狀數組不好嗎
思想:如果當前遍歷到目標節點,就返回節點信息。如果不是,設查詢位置為x,當前結點區間范圍為了[l,r],中點為mid,若x≤mid,則x在左子樹的區間,遞歸當前結點的左孩子;否則x在右子樹的區間,遞歸它的右孩子。
區間修改/查詢
區間查詢:
設待查詢區間為[x,y],如果當前遍歷到的區間被查詢區間包含,就直接返回節點信息。如果不是,設當前結點區間范圍為[l,r]中點為mid,如果x≤mid,則待查詢區間與[l,mid]有交集,遞歸[l,mid];如果y>mid,則待查詢區間與[mid+1,r]有交集,遞歸[mid+1,r],最后將返回的信息合并就是區間[x,y]的答案。
區間修改:
延續區間查詢的思想,我們可以找出所有信息發生改變的節點,可現在有一核心問題:我們并不能直接在這些節點上修改,若全部修改,復雜度難以承受。所以,區間修改的關鍵思路是:只修改對查詢有用的點。為此,我們引入“懶標記"的概念。
懶標記:若遍歷到的節點帶有懶標記,則立即修改當前節點的信息,并把標記下放到左右兒子,然而左右子樹的信息不變動,只有遍歷到左/右節點時才修改其信息
總結
線段樹是很常用強大的數據結構,其功能也遠不止求最值和加和這么簡單,要想真正掌握,還是要背模板 理解其深層的運作機制
練習
洛谷題單傳送門
歡迎各位dl點贊評論,謝謝 收聽 拜讀觀看! awa
總結
- 上一篇: 苹果菜鸟来交作业了,分享Mac如何流畅运
- 下一篇: iQOO 12系列价格曝光 到手价429