[洛穀日報第4期]淺談線段樹——Segment Tree

2020-12-17 洛谷科技

一、簡介線段樹

ps: _此處以詢問區間和為例。實際上線段樹可以處理很多符合結合律的操作。(比如說加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[2])+(a[3]+a[4]))

線段樹之所以稱為「樹」,是因為其具有樹的結構特性。線段樹由於本身是專門用來處理區間問題的(包括RMQ、RSQ問題等。

圖片來源於網際網路。

對於每一個子節點而言,都表示整個序列中的一段子區間;對於每個葉子節點而言,都表示序列中的單個元素信息;子節點不斷向自己的父親節點傳遞信息,而父節點存儲的信息則是他的每一個子節點信息的整合。

有沒有覺得很熟悉?對,線段樹就是分塊思想的樹化,或者說是對於信息處理的二進位化——用於達到O(logn)級別的處理速度,log以2為底。(其實以幾為底都只不過是個常數,可忽略)。而分塊的思想,則是可以用一句話總結為:通過將整個序列分為有窮個小塊,對於要查詢的一段區間,總是可以整合成k個所分塊與m個單個元素的信息的並(0≤k,m≤sqrt{n})。但普通的分塊不能高效率地解決很多問題,所以作為log級別的數據結構,線段樹應運而生。

Extra Tips

其實,雖然線段樹的時間效率要高於分塊但是實際上分塊的總合併次數不會超過sqrt{n}但是線段樹在最壞情況下的合併次數顯然是要大於這個時間效率的qwq。

~~但是畢竟也只是一個很大的常數而已~~

However,雖說如此,分塊的應用範圍還是要廣於線段樹的,因為雖然線段樹好像很快,但是它只能維護帶有結合律的信息,比如區間max/min、sum、xor之類的,但是不帶有結合律的信息就不能維護(且看下文分解);而分塊則靈活得多,可以維護很多別的東西,因為實際上分塊的本質就是優雅的暴力qwq。

其實越暴力的算法可以支持的操作就越多、功能性就越強吶!~~你看n^2的暴力幾乎什麼都可以維護~~

二、逐步分析線段樹的構造實現

1、建樹與維護

由於二叉樹的自身特性,對於每個父親節點的編號i,他的兩個兒子的編號分別是2i和2i+1,所以我們考慮寫兩個O(1)的取兒子函數:

_

Extra Tips

1、此處的inline可以有效防止無需入棧的信息入棧,節省時間和空間。

2、二進位位左移一位代表著數值*2,而如果左移完之後再或上1,由於左移完之後最後一位二進位位上一定會是0,所以|1等價於+1。

~~用二進位運算不是為了裝X,相信我,會快的!~~

那麼根據線段樹的服務對象,可以得到線段樹的維護:

此處一定要注意,push up操作的目的是為了維護父子節點之間的邏輯關係。當我們遞歸建樹時,對於每一個節點我們都需要遍歷一遍,並且電腦中的遞歸實際意義是先向底層遞歸,然後從底層向上回溯,所以開始遞歸之後必然是先去整合子節點的信息,再向它們的祖先回溯整合之後的信息。(這其實是正確性的證明啦)

吶,我們在這兒就能看出來,實際上push_up是在合併兩個子節點的信息,所以需要信息滿足結合律!

那麼對於建樹,由於二叉樹自身的父子節點之間的可傳遞關係,所以可以考慮遞歸建樹(emmmm之前好像不小心劇透了qwq),並且在建樹的同時,我們應該維護父子節點的關係:

2、接下來談區間修改

為什麼不討論單點修改呢qwq?因為其實很顯然,單點修改就是區間修改的一個子問題而已,即區間長度為1時進行的區間修改操作罷了qwq

那麼對於區間操作,我們考慮引入一個名叫「lazy tag」(懶標記)的東西——之所以稱其「lazy」,是因為原本區間修改需要通過先改變葉子節點的值,然後不斷地向上遞歸修改祖先節點直至到達根節點,時間複雜度最高可以到達O(nlogn)的級別。但當我們引入了懶標記之後,區間更新的期望複雜度就降到了O(logn)的級別且甚至會更低.

(1)首先先來從分塊思想上解釋如何區間修改:

分塊的思想是通過將整個序列分為有窮個小塊,對於要查詢的一段區間,總是可以整合成k個所分塊與m個單個元素的信息的並(0<=k,m<=logn)(小小修改了一下的上面的前言qwq)

那麼我們可以反過來思考這個問題:對於一個要修改的、長度為l的區間來說,總是可以看做由一個長度為2 ^ logn 和剩下的元素(或者小區間組成)。那麼我們就可以先將其拆分成線段樹上節點所示的區間,之後分開處理:

如果單個元素被包含就只改變自己,如果整個區間被包含就修改整個區間

其實好像這個在分塊裡不是特別簡單地實現,但是在線段樹裡,無論是元素還是區間都是線段樹上的一個節點,所以我們不需要區分區間還是元素,加個判斷就好。

(2)懶標記的正確打開方式

首先,懶標記的作用是記錄每次、每個節點要更新的值,也就是delta,但線段樹的優點不在於全記錄(全記錄依然很慢qwq),而在於傳遞式記錄:

整個區間都被操作,記錄在公共祖先節點上;只修改了一部分,那麼就記錄在這部分的公共祖先上;如果四環以內只修改了自己的話,那就只改變自己。

畢竟,如果我們採用上述的優化方式的話,我們就需要在每次區間的查詢修改時pushdown一次,以免重複或者衝突或者爆炸qwq

那麼對於pushdown而言,其實就是純粹的pushup的逆向思維(但不是逆向操作):

因為修改信息存在父節點上,所以要由父節點向下傳導lazy tag

那麼問題來了:怎麼傳導pushdown呢?這裡很有意思,開始回溯時執行pushup,因為是向上傳導信息;那我們如果要讓它向下更新,就調整順序,在向下遞歸的時候pushdown不就好惹~qwq:

對於複雜度而言,由於完全二叉樹的深度不超過logn,那麼單點修改顯然是O(logn)的,區間修改的話,由於我們的這個區間至多分logn個子區間,對於每個子區間的查詢是O(1)的,所以複雜度自然是O(logn)~~不過帶一點常數~~

3、那麼對於區間查詢

沒什麼好說的,由於是信息的整合,所以還是要用到分塊思想,我實在是不想再碼一遍了qwq

最後貼~~高清無碼的~~標程:

(還有,輸入大數據一定不要用不加優化的cin/cout啊)

本文發布於洛穀日報,特約作者:皎月半灑花

原文地址:https://pks-loving.blog.luogu.org/senior-data-structure-qian-tan-xian-duan-shu-segment-tree

相關焦點

  • [洛穀日報第46期]線段樹的擴展之淺談zkw線段樹
    0 閱讀本文前請先閱讀【洛穀日報#4】淺談線段樹(https://pks-loving.blog.luogu.org/senior-data-structure-qian-tan-xian-duan-shu-segment-tree)本文主要是上面文章的延伸,所以上文有講的東西本文就不詳細講了QwQ筆者的測試代碼可能寫醜了
  • [洛穀日報第22期]可以代替線段樹的樹狀數組?
    引入大家學了線段樹與樹狀數組後,一定會覺得樹狀數組比線段樹好寫(背)多了,常數也小多了(分析lowbit操作,每次操作中每個節點被訪問的概率是1/2,所以常數是1/2)但是美中不足的是樹狀數組不能區間修改+區間查詢啊。事實上,樹狀數組可以做到這些,還可以查詢第k大(小)值。
  • 線段樹,看這一篇就夠了
    定義線段樹(segment tree),顧名思義, 是用來存放給定區間(segment, or interval)內對應信息的一種數據結構。與樹狀數組(binary indexed tree)相似,線段樹也用來處理數組相應的區間查詢(range query)和元素更新(update)操作。
  • 數據結構-PHP 線段樹的實現
    3.線段樹需要空間分析假設我們把 線段樹 看做是一顆 滿二叉樹,並且不考慮添加元素的情況(即區間固定),對於區間有 n 個元素的數組若 n=2^k(k是正整數) 則需要 2n 的空間,最差的情況是若 n=2^k+1 則需要 4n 的空間,如下圖所示,最下面一層沒有元素的節點使用 null 填充:
  • 【SP14】 Segment Tree 線段樹
    TL; DR 回復 SP14 獲取segment tree視頻連結。
  • [洛穀日報第35期]淺談自適應Simpson法
    0|<15\epsilon 時 S_0 與 S_1+S_2 差別不大(乘以 15 這裡可以按需調整)返回的面積則是 S_1+S_2+\frac{S_1+S_2-S_0}{15}附程序:3 後記這篇文章筆者寫了4h
  • [洛穀日報第53期]淺談一些求近似值的方法
    r]內單調、連續且有f(l)*f(r)<0成立的f(x),做如下操作:計算mid=(l+r)/2若f(l)*f(mid)<0,則令r=mid,否則令l=mid如果達到預定精度,跳轉到4,2 0.618法\優選法 (float\double\long double)可能有的讀者沒聽說過,不過這個方法其實還是很常用的QwQ這個方法更常用於求單峰函數最值,所以這裡以求單峰函數最值為例講解先證明一下它的優越性(過程摘自人教版高中數學選修4-
  • [洛穀日報第79期]二進位與位運算
    是等於 2n+1 的於是我們又回到了 1 ,所以可以得出答案是以 4 為周期循環的。在洛谷上可以找到例題。而狀壓比較常用的操作有更多奇奇怪怪的應用可視題目要求所定。另外別的進位也可以用來做狀態壓縮,但是運算起來就沒那麼方便了,也視情況所定。快速冪快速冪顧名思義,就是快速算某個數的多少次冪。
  • 圖解:什麼是線段樹?
    ); //遞歸處理左子區間    build(mid + right, right, 2 * p + 1); //遞歸處理右子區間        tree[p] = tree[2 * p] + tree[2 * p + 1]; // 合併左右子區間的信息}這裡有一點需要注意:堆式建樹的線段樹的數組大小需要設為原數組大小的
  • [洛穀日報第38期]淺談如何在 Codeforces 下分
    4. 心態刷負是一個漫長的過程。現在 rating 為負的四位選手分別已經參加了21,20,21,30場比賽,而他們掉到負分時最少的選手也已經參加了 場比賽。由此可見,這是一個需要耐心和恆心的過程。給本文發布於洛穀日報,特約作者:OwenOwl原文地址:https://mcfx0.blog.luogu.org/codeforces-negative
  • GGTalk 中有哪些知識 - 線段樹基礎與 RMQ 問題
    由於線段樹也是二叉樹,所以以下所有的表示方式我都使用數組來描述。另外,假設我們的結點是 N 個,根據線段樹結構的性質,我們會把這 N 個點當做葉子結點來看待。由於線段樹是一棵滿二叉樹,假設它有 H 層,第 H 層結點數是 2^(H - 1) 個,一共 2^H - 1 個結點。假設共 S 個結點,我們來推導一下:
  • 數據結構之php實現線段樹
    今天我們一起學習一種新的樹形結構, 線段樹。在我們平時,可能會遇到一些問題,「給定一個數組, 求出數組某段區間的一些性質」。比如給定一個數組 [-2, 0, 3, -5, 2, -1], 求出區間 【0,4】上元素的和,在這個例子中,答案是-2。你可能會說這麼簡單,直接遍歷一下數組不就好了嗎?是的,這樣是確實最簡單,最粗暴的方式。但是我們想一下,這個時間複雜度是多少?
  • [洛穀日報第44期]強勢圖解AC自動機
    完整代碼洛谷AC自動機模板題簡單版(https://www.luogu.org/problemnew/show/P3808)練習洛谷模板題x2完結撒花本文發布於洛穀日報,特約作者:水手hwy原文地址:https://www.luogu.org/blog/42196/qiang-shi-tu-xie-ac-zi-dong-ji
  • 數據結構:線段樹入門與實踐
    最近在刷題的時候,遇到一個涉及到線段樹的問題。之前沒接觸過,看了幾遍題解才看懂。這裡簡單介紹下入門的過程。高級數據結構,線段樹入門一、線段樹的基本思想 線段樹是一種常用來維護區間信息的數據結構,它適用於對區間內進行單點查詢、更新、求最值等操作,且時間複雜度能控制到 O(logN)。
  • 給球上色(線段樹+離散化) - HDU 1199
    其中lower_bound返回第1個不小於b[i]的值的指針,而upper_bound返回第1個大於b[i]的值的指針,當然在這個題中也可以用lower_bound然後再加1得到與upper_bound相同結果,兩者都是針對以排好序列。使用STL離散化大大減少了代碼量且結構相當清晰。
  • [洛穀日報第45期]談談關於初賽的那些事
    下面主要提一下數學知識和CCF讚歌相關的,相信大家在其他的洛穀日報裡能夠看到許多圖論與數據結構相關的內容,而硬體知識與計算機發展史也應該已經有所了解。例題:由0,1,2,3,4,5可以組成多少個沒有重複數字五位奇數.由於末位和首位有特殊要求,應該優先安排,以免不合要求的元素佔了這兩個位置.
  • [洛穀日報第29期]OI中可以用到的Linux基礎教程
    Linux基礎教程●前置系統:任意Linux(不用NOI Linux也沒問題,我用的是deepin),如果沒有裝,而且你用的是win10,請看往期的洛穀日報:練習Linux?下面fa♂一張我的vim:(4)sublime等編輯器,這些NOI Linux不自帶,還是不講。等一等,我們寫完代碼了,該怎麼編譯?①先打開終端,在終端裡打開你的原始碼所在的目錄。
  • 矽谷面試經典算法之-線段樹、樹狀數組
    矽谷面試經典算法之-線段樹、樹狀數組線段樹又叫Segmentation Tree,樹狀數組又叫Binary indexed tree
  • [洛穀日報第19期]Codeforces遊玩攻略
    3.2 社區通過以下幾種方式,您可以查看文章:(1) 單擊首頁置頂文章或者TOP菜單中的文章的標題(2) 直接輸入網址(3) 通過側邊欄最後的"Recent actions"(4)比賽種類Codeforces上舉行的比賽一般有4種,分別是Div.1,Div.2,Div.3和Educational Round。先講講Educational Round,Educational Codeforces Round一般題目較多,採用擴展ACM-ICPC賽制,即提交代碼立即出結果,錯誤一次計10分鐘罰時。
  • [洛穀日報第59期]我有獨特的騙分技巧
    不急,來看道題:洛谷P1463 POI2002,HAOI2007 反素數(https://www.luogu.org/problemnew/show/P1463)這道題可謂是經典的打表題(個人覺得搜索簡單些),但是也不能無腦直接『打出答案』,比如我一開始的打表思路就是如下的偽代碼: