【2024-ZR-C Day 6】資料結構(4):樹(重、長)鏈剖分、虛樹、dsu on tree

心灵震荡發表於2024-07-24

1. 樹鏈剖分

1.1. 重鏈剖分

按照子樹大小劃分,令子樹大小最大的兒子重兒子,將所有不是重兒子的兒子稱為輕兒子。
斷開每個點與輕兒子之間的邊(輕邊),剩下由重邊每個點和重兒子形成的邊)形成的鏈稱為重鏈

image
(以上是重鏈剖分示意圖。)

性質:樹上任意一點到根路徑上最多經過 \(O(\log_2 n)\) 條重鏈。

1.2. 長鏈剖分

與重鏈剖分類似,但是重兒子的確定標準由子樹大小最大改為子樹深度最大。

image

(事實上【1.1. 重鏈剖分】中的重剖示意圖同樣適用於長鏈剖分。)

2. 虛樹

欽定若干個關鍵點,刪除與關鍵點無關的點、邊,剩下的每個節點要麼是關鍵點,要麼是兩個關鍵點的 LCA的一顆樹被稱為虛樹

性質:虛樹中兒子個數 \(\le 1\) 的點即為關鍵點。

3. 例題

3.1. LOJ6669 Nauuo and Binary Tree

有一棵以 \(1\) 為根的二叉樹,你可以詢問任意兩點之間的距離,求出每個點的父親。
節點數不超過 \(3000\),你最多可以進行 \(30000\) 次詢問。

考慮樹鏈剖分的性質,我們知道 \(n\) 個點的樹上,重鏈的數量一定不會太多,約為 \(\log n\) 條。

先用 \(2999\) 次詢問得到每個點的深度。

考慮按深度加入每個點,這樣做可以保證我們每時每刻都能維護出一顆連通的樹。
每一次加點前,我們對已經構建完成的部分做重鏈剖分,求出所有重鏈最底端的節點。

每次加入一個點時,詢問當前所有的重鏈底到新點的距離,距離為 \(1\) 則找到了父親。

3.2. CF1009F Dominant Indicesnb

給出一顆 \(n\)\(n \le 10^6\)) 個節點的樹,樹根為 \(1\). 設 \(d(x, k)\)\(x\) 子樹中到 \(x\) 距離為 \(k\) 的節點數。
對於每個 \(x\),求最小的 \(k\) 使得 \(d(x, k)\) 最大。

考慮狀態轉移為 \(\displaystyle d_{u, k} = \sum_{v \in G_u} d_{v, k + 1}\),總時間複雜度 \(O(n^2)\).

\(len_u\) 表示 \(u\) 開頭的最長鏈的長度。考慮長鏈剖分最佳化,使用指標,每次 \(O(1)\) 地將重兒子的答案轉移到當前節點,再對所有輕兒子進行暴力地轉移即可。時間複雜度 \(O(n)\).

3.3. CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

一棵根為 \(1\) 的樹,每條邊上有一個字元(\(a\)\(v\)\(22\) 種)。一條簡單路徑被稱為 Dokhtar-kosh,當且僅當路徑上的字元經過重新排序後可以變成一個迴文串。 求每個子樹中最長的 Dokhtar-kosh 路徑的長度。

由於可以任意重排構成迴文串的充要條件是不超過一種字母的出現次數是奇數,因此我們只關心出現次數的奇偶性。
可以用一個 \(22\) 位二進位制數表示每種字母出現次數奇偶性。

顯然有 \(val(u, v) = val(u, 1) \oplus val(v, 1)\).
\(d_u = val(u, 1)\),我們的任務轉化為統計 \(\displaystyle \max_{u, v, d_u \oplus d_v \in \{0\} \cup \{2^k | k \in [0, 21]\}} dis_{u, v}\).

而熟知的是 \(dis_{u, v} = dep_u + dep_v - 2 \times dep_{lca}\).
所以可以對於每個 LCA 統計答案。

暴力的做法是:列舉 LCA,每次加入一顆子樹,就對其中的所有點,列舉所有 \(x \oplus d_u \in \{0, 1, 2, 4, \cdots, 2^{21}\}\)\(x\),用 \(dep_u + f_x - 2 \times dep_{lca}\).
完成答案更新後,用子樹中的點更新 \(f\).
最劣時間複雜度 \(O(n^2)\).

考慮最佳化。

我們知道每個點到根經過的重鏈條數是 \(O(\log n)\),且在處理某個 LCA 時,加入的第一個子樹只會和 LCA 本身更新答案。
上面的性質意味著,我們可以選擇一個兒子,在統計完以它為 LCA 的答案後,無需清空 \(f\) 陣列,可以直接繼承。
然後統計當前節點和這個子樹對答案的貢獻,並令 \(f_{d_u} = \max(f_{d_u}, dep_u)\).

這樣做與我們初始化 \(f_{d_u} = dep_u\),其餘均為 \(-inf\),然後遍歷該子樹是等價的。

如果我們欽定這個兒子是重鏈剖分中的重兒子,那就意味著我們除了每個點作為 LCA 要遍歷一次以外的每次遍歷,都因為它在某個點的輕兒子子樹中。
這樣的情形只在根到 \(u\) 路徑上換重鏈時發生,也就是不超過 \(\log n\) 次。

因此可以做到 \(O(Vn \log n)\),其中 \(V\) 是值域,在這裡的值為 \(22\).

相關文章