線段樹套線段樹
常見用途
在算法競賽中,我們有時需要維護多維度信息。在這種時候,我們經常需要樹套樹來記錄信息。
實現原理
我們考慮用樹套樹如何實現在二維平面上進行單點修改,區域查詢。我們考慮外層的線段樹,最底層的 \(1\) 到 \(n\) 個節點的子樹,分別代表第 \(1\) 到第 \(n\) 行的線段樹。那麼這些底層的節點對應的父節點,就代表其兩個子節點的子樹所在的一片區域。
性質
空間複雜度
通常情況下,我們不可能對於外層線段樹的每一個結點都建立一顆子線段樹,空間需求過大。樹套樹一般採取動態開點的策略。單次修改,我們會涉及到外層線段樹的 \(\log{n}\) 個節點,且對於每個節點的子樹涉及 \(\log{n}\) 個節點,所以單次修改產生的空間最多為 \(\log^2{n}\)。
時間複雜度
對於詢問操作,我們考慮我們在外層線段樹上進行 \(\log{n}\) 次操作,每次操作會在一個內層線段樹上進行 \(\log{n}\) 次操作,所以時間複雜度為 \(\log^2{n}\)。
修改操作,與詢問操作複雜度相同,也為 \(\log^2{n}\)。
經典例題
陌上花開 將第一維排序處理,然後用樹套樹維護第二維和第三維。
示例代碼
第二維查詢
| int tree_query(int k, int l, int r, int x) {
if (k == 0) return 0;
if (1 <= l && r <= sec[x].y) return vec_query(ou_root[k], 1, p, 1, sec[x].z);
int mid = l + r >> 1, res = 0;
if (1 <= mid) res += tree_query(ou_ch[k][0], l, mid, x);
if (sec[x].y > mid) res += tree_query(ou_ch[k][1], mid + 1, r, x);
return res;
}
|
第二維修改
| void tree_insert(int &k, int l, int r, int x) {
if (k == 0) k = ++ou_tot;
vec_insert(ou_root[k], 1, p, sec[x].z);
if (l == r) return;
int mid = l + r >> 1;
if (sec[x].y <= mid)
tree_insert(ou_ch[k][0], l, mid, x);
else
tree_insert(ou_ch[k][1], mid + 1, r, x);
}
|
第三維查詢
| int vec_query(int k, int l, int r, int x, int y) {
if (k == 0) return 0;
if (x <= l && r <= y) return data[k];
int mid = l + r >> 1, res = 0;
if (x <= mid) res += vec_query(ch[k][0], l, mid, x, y);
if (y > mid) res += vec_query(ch[k][1], mid + 1, r, x, y);
return res;
}
|
第三維修改
| void vec_insert(int &k, int l, int r, int loc) {
if (k == 0) k = ++tot;
data[k]++;
if (l == r) return;
int mid = l + r >> 1;
if (loc <= mid) vec_insert(ch[k][0], l, mid, loc);
if (loc > mid) vec_insert(ch[k][1], mid + 1, r, loc);
}
|
相關算法
面對多維度信息的題目時,如果題目沒有要求強制在線,我們還可以考慮 CDQ 分治,或者 整體二分 等分治算法,來避免使用高級數據結構,減少代碼實現難度。
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:Chrogeek, HeRaNO, Dev-XYS, Dev-jqe
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用