1. 程式人生 > 其它 >7.9&7.11 DS

7.9&7.11 DS

\[\begin{aligned}\begin{gathered}\huge\cal{D}\textsf{ata}\ \huge\cal{S}\textsf{tructures}\ \large\rm{-ZH\_comld} \end{gathered}\end{aligned} \]
  • 資料結構要形成自己的寫法。

樹狀陣列

一維樹狀陣列

一些注意事項:

  • 下標從 \(1\) 開始

  • \(\operatorname{lowbit}(x)=x\&-x\)

二維樹狀陣列

  • 區間修改 / 查詢時對兩維分別列舉 \(\operatorname{lowbit}\)

樹狀陣列上二分

  • 與線段樹二分類似,判斷當前狀態對最終答案的貢獻,根據大小進行二分。

線段樹

線段樹合併

  • 把很多權值線段樹合併起來

兩種做法:

  • 對詢問合理排序,合併時把一棵樹合併到另一棵樹上並使得詢問都能得到答案 (空間小) 。
  • 每次合併新開節點,不需要離線 (空間大) 。

可持久化線段樹 (主席樹)

  • 靜態區間第 \(k\)
  • 區間 \(mex\) (沒有出現的最小自然數)

動態開點,每次單點操作最多隻對 \(\lceil\log n\rceil\) 個節點有影響,於是新建 \(\lceil\log n\rceil\) 個節點進行維護。

時間複雜度:\(O(n\log n)\)

典例:

掃描線

  • 求矩形面積並 / 周長並

面積並:先把所有橫向邊對 \(y\) 座標排序,再把所有端點在 \(x\) 軸上進行離散化,對中間的所有線段開一棵線段樹維護該線段內被整個圖形所截的長度,掃描時進行維護。

周長並:

樹鏈的並

  • \(n\) 個點的樹,給定 \(k\) 個點,求它們到根的邊權並。

先按 DFS 序排序,答案即為:

\[\sum_{i=1}^k dis_{k_i,root}-\sum_{i=1}^{k-1} dis_{lca_{k_i,k_{i+1}},root} \]

虛樹

  • 給定一棵 \(n\) 個節點的樹 \(T\),構造一棵新的樹 \(T'\) 使其包含指定的 \(k\) 個節點和他們的 LCA,並使總節點數最小。

樹鏈剖分

  • 一個會讓你程式碼長度增加 \(1kb\) 的資料結構。

分塊

  • 是一種暴力的優化。

對長度為 \(n\) 的序列分為 \(\sqrt n\) 個長度為 \(\sqrt n\) 的塊,每一次操作只需分為整塊的操作和單點的操作。

時間複雜度:\(O(n\sqrt n)\)

莫隊

  • 是一種基於分塊的演算法。

樸素莫隊

  • \(k\) 次詢問,每次詢問 \([l,r]\) 內的顏色個數。

適用範圍:從 \([l,r]\)\([l+1,r]\) 的轉換比較快捷。

先對序列分塊,再對所有詢問按 \(l,r\) 排序,從一個詢問到下一個詢問只需要暴力拓展邊界即可。

時間複雜度:\(O(n\sqrt n)\)

帶修莫隊

  • 同上,但要支援單點修改。

對於修改的操作,實際上只是增加了一個時間軸,此時對於每次詢問按 \(l,r,t\) 排序求解。

注意分塊大小為 \(n^{\frac{2}{3}}\),總時間複雜度:\(O(n^{\frac{5}{3}})\)

回滾莫隊

適用條件:區間轉移時無法實現增加 / 刪除。

笛卡爾樹

  • 一種特定的二叉資料結構。
  • 具有堆的性質,滿足父節點比兒子節點大。
  • 這棵樹的中序遍歷就是原來的序列。

建樹做法:單調棧維護右鏈,時間複雜度 \(O(n)\)

for(int i=1;i<=n;i++){
    while(top&&a[st[top]]>=a[i])
        	ch[i][0]=st[top--];
   	if(top) ch[st[top][1]]=i;
    st[++top]=i;
}

基環樹

  • \(n\) 個點,\(n\) 條邊的連通圖,環上的每個點ac都是其子樹的樹根。
  • 對於有向圖上的基環樹,分為內向基環樹和外向基環樹。

二叉查詢樹 (Binary Search Tree)

  • 中序遍歷等同於原序列。
  • 可以在某一位置插入,在 BST 上查詢前驅後繼。

Treap

  • 堆和 BST 結合的平衡樹。
  • 旋轉操作:在不改變原查詢樹的中序遍歷下將一個節點向上翻轉。

插入:在 BST 上尋找應該插入的位置 (類比線段樹上二分) 。

刪除:將所要刪除的節點旋轉到葉子再刪除。

右旋:先將根的左兒子的右兒子連向根,再將根連向根的左兒子,最後重新維護所改變的節點。

左旋:先將根的右兒子的左兒子連向根,再將根連向根的右兒子,最後重新維護所改變的節點。

Splay

  • 每次對一個點操作完成後要把這個點旋轉到根節點的位置來保證複雜度。

插入:與 Treap 類似,找到合適的點插入後將其旋轉到根。

刪除:將根的左兒子的右兒子旋轉到左邊,再將根的右兒子連到根的左兒子右邊,最後刪除根。

雙旋:每次用雙旋將一個節點旋轉到根的位置。

FHQ Treap (無旋 Treap)

  • BST + Treap

最方便的平衡樹 (STL)

std::set  std::map

點分治

  • 主要用來求解樹上路徑問題。
  • 樹上路徑總數很多,但如果確定一個分治中心,然後將其子樹內的路徑合併就會變得很快。
  • 怎麼選分治中心 —— 就是樹的重心。
  • 選擇樹的重心後,分治下去的子樹大小至少會變成原來的一半,所以最多會分治 \(\log n\) 層,總時間複雜度 \(O(n\log n)\)

邊分治

contact me on QQ (601585974 布魯)