跳转至

線段樹套平衡樹

常見用途

在算法競賽中,我們有時需要維護多維度信息。在這種時候,我們經常需要樹套樹來記錄信息。當需要維護前驅,後繼,第 \(k\) 大,某個數的排名,或者插入刪除的時候,我們通常需要使用平衡樹來滿足我們的需求,即線段樹套平衡樹。

過程

我們以 二逼平衡樹 為例,來解釋實現原理。

關於樹套樹的構建,我們對於外層線段樹正常建樹,對於線段樹上的某一個節點,建立一棵平衡樹,包含該節點所覆蓋的序列。具體操作時我們可以將序列元素一個個插入,每經過一個線段樹節點,就將該元素加入到該節點的平衡樹中。

操作一,求某區間中某值的排名:我們對於外層線段樹正常操作,對於在某區間中的節點的平衡樹,我們返回平衡樹中比該值小的元素個數,合併區間時,我們將小的元素個數求和即可。最後將返回值 \(+1\),即為某值在某區間中的排名。

操作二,求某區間中排名為 \(k\) 的值:我們可以採用二分策略。因為一個元素可能存在多個,其排名為一區間,且有些元素原序列不存在。所以我們採取和操作一類似的思路,我們用小於該值的元素個數作為參考進行二分,即可得解。

操作三,將某個數替換為另外一個數:我們只要在所有包含某數的平衡樹中刪除某數,然後再插入另外一個數即可。外層依舊正常線段樹操作。

操作四,求某區間中某值的前驅:我們對於外層線段樹正常操作,對於在某區間中的節點的平衡樹,我們返回某值在該平衡樹中的前驅,線段樹的區間結果合併時,我們取最大值即可。

性質

空間複雜度

我們每個元素加入 \(O(\log n)\) 個平衡樹,所以空間複雜度為 \(O((n + q)\log{n})\)

時間複雜度

  • 對於 1,3,4 操作,我們考慮我們在外層線段樹上進行 \(O(\log{n})\) 次操作,每次操作會在一個內層平衡樹樹上進行 \(O(\log{n})\) 次操作,所以時間複雜度為 \(O(\log^2{n})\)
  • 對於 2 操作,多一個二分過程,為 \(O(\log^3{n})\)

經典例題

二逼平衡樹 外層線段樹,內層平衡樹。

實現

平衡樹部分代碼請參考 Splay 等其他條目。

操作一:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int vec_rank(int k, int l, int r, int x, int y, int t) {
  if (x <= l && r <= y) {
    return spy[k].chk_rank(t);
  }
  int mid = l + r >> 1;
  int res = 0;
  if (x <= mid) res += vec_rank(k << 1, l, mid, x, y, t);
  if (y > mid) res += vec_rank(k << 1 | 1, mid + 1, r, x, y, t);
  if (x <= mid && y > mid) res--;
  return res;
}

操作二:

1
2
3
4
5
6
7
8
9
int el = 0, er = 100000001, emid;
while (el != er) {
  emid = el + er >> 1;
  if (vec_rank(1, 1, n, tl, tr, emid) - 1 < tk)
    el = emid + 1;
  else
    er = emid;
}
printf("%d\n", el - 1);

操作三:

1
2
3
4
5
6
7
8
9
void vec_chg(int k, int l, int r, int loc, int x) {
  int t = spy[k].find(dat[loc]);
  spy[k].dele(t);
  spy[k].insert(x);
  if (l == r) return;
  int mid = l + r >> 1;
  if (loc <= mid) vec_chg(k << 1, l, mid, loc, x);
  if (loc > mid) vec_chg(k << 1 | 1, mid + 1, r, loc, x);
}

操作四:

1
2
3
4
5
6
7
8
int vec_front(int k, int l, int r, int x, int y, int t) {
  if (x <= l && r <= y) return spy[k].chk_front(t);
  int mid = l + r >> 1;
  int res = 0;
  if (x <= mid) res = max(res, vec_front(k << 1, l, mid, x, y, t));
  if (y > mid) res = max(res, vec_front(k << 1 | 1, mid + 1, r, x, y, t));
  return res;
}

相關算法

面對多維度信息的題目時,如果題目沒有要求強制在線,我們還可以考慮 CDQ 分治,或者 整體二分 等分治算法,來避免使用高級數據結構,減少代碼實現難度。