動態樹分治
動態點分治
動態點分治用來解決 帶點權/邊權修改 的樹上路徑信息統計問題。
點分樹
回顧點分治的計算過程。
對於一個結點 \(x\) 來説,其子樹中的簡單路徑包括兩種:經過結點 \(x\) 的,由一條或兩條從 \(x\) 出發的路徑組成的;和不經過結點 \(x\) 的,即已經包含在其所有兒子結點子樹中的路徑。
對於一個子樹中簡單路徑的計算,我們選擇一個分治中心 \(rt\),計算經過該節點的子樹中路徑的信息,然後對於其每個兒子結點,將刪去 \(rt\) 後該點所在連通塊作為一個子樹,遞歸計算。選擇的分治中心點可以構成一個樹形結構,稱為 點分樹。我們發現,計算點分樹中同一層的結點所代表的連通塊(即以該結點為分治中心的連通塊)的大小總和是 \(O(n)\) 的。這意味着,點分治的時間複雜度是與點分樹的深度相關的,若點分樹的深度為 \(h\),則點分治的複雜度為 \(O(nh)\)。
可以證明,當我們每次選擇連通塊的重心作為分治中心的時候,點分樹的深度最小,為 \(O(\log n)\) 的。這樣,我們就可以在 \(O(n\log n)\) 的時間複雜度內統計樹上 \(O(n^2)\) 條路徑的信息了。
由於樹的形態在動態點分治的過程中不會改變,所以點分樹的形態在動態點分治的過程中也不會改變。
下面給出求點分樹的參考代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
實現修改
在查詢和修改的時候,我們在點分樹上暴力跳父親修改。由於點分樹的深度最多是 \(O(\log n)\) 的,所以這樣做複雜度能得到保證。
在動態點分治的過程中,需要一個結點到其點分樹上的祖先的距離等其他信息,由於一個點最多有 \(O(\log n)\) 個祖先,我們可以在計算點分樹時額外計算深度 \(dep[x]\) 或使用 LCA,預處理出這些距離或實現實時查詢。注意:一個結點到其點分樹上的祖先的距離不一定遞增,不能累加!
在動態點分治的過程中,一個結點在其點分樹上的祖先結點的信息中可能會被重複計算,這是我們需要消去重複部分的影響。一般的方法是對於一個連通塊用兩種方式記錄:一個是其到分治中心的距離信息,另一個是其到點分樹上分治中心父親的距離信息。這一部分內容將在例題中得到展現。
例題 「ZJOI2007」捉迷藏
給定一棵有 \(n\) 個結點的樹,初始時所有結點都是黑色的。你需要實現以下兩種操作:
- 反轉一個結點的顏色(白變黑,黑變白);
-
詢問樹上兩個最遠的黑點的距離。
\(n\le 10^5,m\le 5\times 10^5\)
求出點分樹,對於每個結點 \(x\) 維護兩個 可刪堆。\(dist[x]\) 存儲結點 \(x\) 代表的連通塊中的所有黑點到 \(x\) 的距離信息,\(ch[x]\) 表示結點 \(x\) 在點分樹上的所有兒子和它自己中的黑點到 \(x\) 的距離信息,由於本題貪心的求答案方法,且兩個來自於同一子樹的路徑不能成為一條完成的路徑,我們只在這個堆中插入其自己的值和其每個子樹中的最大值。我們發現,\(ch[x]\) 中最大的兩個值(如果沒有兩個就是所有值)的和就是分治時分支中心為 \(x\) 時經過結點 \(x\) 的最長黑端點路徑。我們可以用可刪堆 \(ans\) 存儲所有結點的答案,這個堆中的最大值就是我們所求的答案。
我們可以根據上面的定義維護 \(dist[x],ch[x],ans\) 這些可刪堆。當 \(dist[x]\) 中的值發生變化時,我們也可以在 \(O(\log n)\) 的時間複雜度內維護 \(ch[x],ans\)。
現在我們來看一下,當我們反轉一個點的顏色時,\(dist[x]\) 值會發生怎樣的改變。當結點原來是黑色時,我們要進行的是刪除操作;當結點原來是白色時,我們要進行的是插入操作。
假如我們要反轉結點 \(x\) 的顏色。對於其所有祖先 \(u\),我們在 \(dist[u]\) 中插入或刪除 \(dist(x,u)\),並同時維護 \(ch[x],ans\) 的值。特別的,我們要在 \(ch[x]\) 中插入或刪除值 \(0\)。
參考代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
例題 Luogu P6329【模板】點分樹 | 震波
給定一棵有 \(n\) 個結點的樹,樹上每個結點都有一個權值 \(v[x]\)。實現以下兩種操作:
- 詢問與結點 \(x\) 距離不超過 \(y\) 的結點權值和;
- 修改結點 \(x\) 的點權為 \(y\),即 \(v[x]=y\)。
我們用動態開點權值線段樹記錄距離信息。
類似於上題的思路,對於每個結點,我們維護線段樹 \(dist[x]\),表示分治塊 \(x\) 中的所有結點到結點 \(x\) 的距離信息,下標為距離,權值加上點權。線段樹 \(ch[x]\) 表示分治塊 \(x\) 中所有結點到結點 \(x\) 在分治樹上的父親結點的距離信息。
在本題中,所有查詢和修改都需要在點分樹上對所有祖先進行修改。
以查詢操作為例,如果我們要查詢距離結點 \(x\) 不超過 \(y\) 的結點的權值和,我們要先將答案加上線段樹 \(dist[x]\) 中下標從 \(0\) 到 \(y\) 的權值和,然後我們遍歷 \(x\) 的所有祖先 \(u\),設其低一級祖先為 \(v\),令 \(d=dist(x,u)\),如果我們不進入包含 \(x\) 的子樹,即以 \(v\) 為根的子樹,那麼我們要將答案加上線段樹 \(dist[u]\) 中下標從 \(0\) 到 \(y-d\) 的權值和。由於我們重複計算了以 \(v\) 為根的部分,我們要將答案減去線段樹 \(ch[v]\) 中下標從 \(0\) 到 \(y-d\) 的權值和。
在進行修改操作時,我們要同時維護 \(dist[x]\) 和 \(ch[x]\)。
參考代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用