segment tree beats
通俗來說一般把這一類問題成為 segment tree beats
,可以簡稱為 beats
。
區間最值操作
例題零
- 維護一個序列,支援區間加,區間對一個數 \(v\) 取 \(\min\),單點求值。
直接線上段樹維護標記即可,需要注意一下加法操作和取 \(\min\) 操作的順序。
例題一
- 維護一個序列,支援區間對一個數 \(v\) 取 \(\min\),區間求和。
似乎不太能線上段樹上直接維護標記,考慮直接把遞迴到的區間分成三類:
- 區間最大值 \(mx\leq v\),直接返回即可。
- 區間嚴格次大值 \(smx<v\leq mx\),此時如果記區間內的最大值個數為 \(cmx\),所有數的和為 \(sum\),那麼影響就是 \(sum\leftarrow sum-cmx(mx-v),mx=v\),我們可以直接線上段樹上維護標記,含義是把區間內的最大值變成了什麼,這個顯然可以下傳。
- 否則就繼續遞迴。
看起來非常暴力,分析一下複雜度,每次修改一個節點一定會讓他的最大值和次大值合併,因此複雜度不會超過所有結點的區間長度和,也就是 \(\Theta(n\log n)\)。
例題二
- 維護一個序列,支援區間加,對一個數 \(v\) 取 \(\min\),區間求和。
在上一題的基礎上只需要記錄區間內的加法標記即可,論文中證明了這樣子的複雜度是 \(\Theta(n\log^2 n)\)。
例題三 BZOJ4695 最假女選手
大致思路差不多,只是麻煩了億點點,需要同時維護最大值,最小值,嚴格次大值,嚴格次小值,以及各種標記,複雜度同上。
例題四 CTSN loves segment tree
大致思路差不多,只是麻煩了億點點,需要同時維護 \(i\) 是(不是)\(A_i\) 裡最大值,\(j\) 是(不是) \(B_j\) 裡最大值的 \(A_i+B_j\) 的最大值,複雜度同上。
例題五 【UR #19】前進四
例題六 CF1290E Cartesian Tree
歷史最值問題
歷史最值問題一般分成三類,歷史最大值,歷史最小值,歷史版本和,以歷史版本和為例,其描述為:維護序列 \(A,B\),並在每次操作過後,令 \(B_i=B_i+A_i\),然後查詢 \(B_i\) 的區間和。
\(B\) 就是 \(A\) 序列的歷史版本和。
例題一
- 維護序列 \(A,B\),對 \(A\) 做區間加,並在每次操作過後,令 \(B_i=B_i+A_i\),然後查詢 \(B_i\) 的區間和。
我們考慮在每個線段樹節點都用一個佇列維護所有標記(區間加標記,以下記為 add
標記;記錄歷史版本和標記,以下記為 his
標記),然後下傳的時候就合併佇列,這樣當然沒問題,不過複雜度爆炸,我們考慮簡化一下這個標記佇列,找出真正關心的量。
我們維護每個節點的 \(A\) 的和 \(sumA\) 以及 \(B\) 的和 \(sumB\),當從上面傳下來一個新的標記序列 \(S\) 時,\(sumA\) 的變化顯然是加上 \((\) \(S\) 中的 add
標記的和 \()\times(\)區間長度 \(len)\)。
而 \(B\) 的變化應該是:加上 $sumA\times $$(S$ 中 his
標記的數量),加上( \(S\) 中每次 his
操作前的 add
操作的和)\(\times len\)。
因此我們只需要維護標記序列中 add
操作的和,his
標記的數量,每次 his
操作前的 add
操作的和這三個量即可,複雜度 \(\Theta(n\log n)\)。
對於歷史最大值/最小值操作,是類似的,可以自己推一下需要維護什麼量。
例題二 【模板】線段樹 3(區間最值操作、區間歷史最值)
此題還有對 \(A\) 序列的取 \(\min\) 操作,這一部分可以用一開始的做法解決,但是噁心的地方在於這樣子需要對於最大值以及非最大值的位置維護兩類標記,實現起來更為麻煩。
例題三 P4314 CPU 監控
和上一題沒有什麼太大區別。
歷史最值問題的常見應用
給出序列 \(A\) 以及某種關於區間 \([l,r]\) 的函式值 \(f(l,r)\),\(m\) 次詢問,每次給出 \(L,R\),求出 \(\sum\limits_{L\leq l\leq r\leq R} f(l,r)\)。
例如下面的題:
例題四 [NOIP2022] 比賽
本題中 \(f(l,r)=(\max\limits_{i=l}^{i=r}A_i)(\min\limits_{i=l}^rB_i)\)。
這一類問題的通用做法是這樣的:
- 對序列掃描線,掃到 \(i\) 時線段樹上 \(j\) 位置維護 \(f(j,i)\),那麼 \(\sum\limits_{L\leq l\leq r\leq R} f(l,r)\) 就是當掃到 \(i\) 時區間 \([l,r]\) 內所有位置的歷史和。
對於本題,我們需要對於所有左端點 \(j\) 維護 \(f(j,i)\),考慮維護 \(A,B\) 的單調棧,這樣每次加入 \(i+1\) 時就是彈出若干棧頂元素。
考慮對於每個 \(j\) 維護 \(p_j,q_j\) 表示 \(A\) 序列的字尾 \(\max\) 以及 \(B\) 序列的字尾 \(\min\),那麼操作就是對 \(p,q\) 做區間賦值,維護 \(p_i\times q_i\) 的歷史和 \(s_i\)。
處理一下就能維護出 \(f(l,r)\) 的和以及歷史和了。
- 一種更加好寫的寫法(但是常數更大)
以例題四為例,我們維護向量 \([1,p_i,q_i,p_iq_i,s_i]\),那麼可以發現賦值操作可以寫成矩陣乘法的形式,這樣子可以減少大量的細節處理,當然代價是常數更大。
例題五:CF1824D LuoTianyi and the Function
較為板子。
例題六 P9990 [Ynoi Easy Round 2023] TEST_90
較為板子。
例題七 [Ynoi2003] 鈴原露露
板子的地方很板子。
例題八 [Ynoi2009] rprmq1
太牛的題。