KI子線段樹 / AKEE SegmentTree


背景

你 Ki 叔 最近 CF 虐場的同時發明了一種趣味的東西,適用於區間修改查詢問題,但合並兩個區間的貢獻復雜度需要與區間長度有關的問題,這種問題無法用普通線段樹去維護,因為復雜度爆炸,過去一般會使用分塊維護,需要討論散塊、整塊等問題,較為復雜,而神仙大 Ki 子研究出了一種新的方式,代碼好寫,想法基於在線段樹上對節點大小進行根號分治。

理論

理論具體來說:

  • 設立一個閾值 \(B\)
  • 修改的時候,對於線段樹節點大小 \(\le B\) 的節點,暴力 pushup,\(> B\) 的時候則不管。
  • 查詢的時候,只有當前節點完全屬於查詢區間且節點大小 \(\le B\) 時,在該點上相應查詢。

寫起來很好寫,就 pushup 和 query 的時候加一個 if 條件就行。

事實上,相當於是舍棄了線段樹上方一部分的結構,也可以理解維護了 \(\frac{n}{B}\) 級別顆線段樹。

這樣做復雜度是啥子?設 pushup 一個長度為 \(a\) 的區間的復雜度是 \(O(a \times X)\),查詢一個線段樹節點的答案復雜度是 \(O(Y)\)

  • 考慮 Pushup。根據線段樹理論每層只會遞歸到兩個節點,因此區間總長度是 \(B + \frac{B}{2} + \frac{B}{4} + \dots = 2B\) 級別的,復雜度是 \(O(B \times X)\)
  • 考慮 Query,定義最高層是節點大小 \(\le B\) 最大的那一層,遞歸到最高層下面的最多兩個區間,復雜度應當是 \(O((\frac{n}{B} + \log B) \times Y)\),通常 \(\log\) 要小,可以視為 \(O(\frac{n}{B} \times Y)\)

這個均攤大概是 \(B = \sqrt \frac{nY}{X}\),總復雜度 \(O(q\sqrt{nXY})\)

例 1:CF1540D. Inverse Inversions

題目

經過若干步轉化,問題變為維護一個序列 \(b\),若干次查詢:

  • \(b\) 單點改

  • 給一個值 \(v\),以及 \(p\),執行:

    for i = p; i <= n; i++:
    	if v >= b[i]: v++
    

    輸出最后的 \(v\)

考慮進行一段區間的這樣操作,設 \(f(x)\) 為把 \(x\) 丟出去出來的會是啥,這個函數是連續不降的,函數不同的值只有區間長度種,可以分段維護函數,考慮合並的時候可以用類歸並的方式 \(O(長度)\) 合並。

查詢的時候直接按順序在維護分段函數上二分 / lower_bound 就行。

這樣復雜度是 \(O(q\sqrt{n \log n})\)

ki 叔代碼

例 2:CF1129D Isolation / [BJOI2017]開車

鏈接 1 / 鏈接 2

兩題最后可以轉化為這樣一個數據結構問題:

\(n\) 個東西排成一排 ,有兩個屬性 \(a_i, b_i\),每次:

  • \([l, r]\) 區間的 \(a\) 加減一個權值 \(w\)
  • 詢問一個區間 \([l, r]\) 中,所有 \(a\) 在區間 \([x, y]\)\(b\) 的和。

對於一個區間,可以維護按 \(a\) 排序的結果並且預處理 \(b\) 前綴和,查詢就可以在上面二分做到 \(\log\),而排序可以從下面兩個兒子線性歸並排序。

復雜度是 \(O(q\sqrt{n\log n})\) 的。

這比傳統的分塊,把 \(\log\) 放根號里了,但事實上原本也可以做到,只不過仍然需要在散塊中歸並排序,其實與 ki 子線段樹原理本質是一樣的,但這個統一且和諧,簡單很多!

這兩題確實也有沒有 \(\log\) 做法,是開一個桶,但依賴於相鄰兩個值差不超過 \(1\) 這種限制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM