Treap
性質¶
- Key 具有樹性質
- 左子樹 < 根
- 右子樹 > 根
- Priority 具有堆性質
- 父節點 > 子節點
Treap 的高度在期望下是 \(O(\log n)\)
定義 \(H(n)\) 為 \(n\) 個 node 的平均樹高,目前 Treap 的 key 中序是 \(k_1,\ldots,k_n\)
我們將 \(k_1,\ldots,k_n\) 利用四分位距切成四塊
平均上有 \(\displaystyle \frac{1}{2}\) 的機率,root 會切在中間兩塊,這時 worst case 會是切在最邊邊的地方(這樣其中一邊的點數會特別多),高度只須高度看比較高的子樹,所以 \(\displaystyle H(n)=H(\frac{3}{4}n)+1\)
平均上有 \(\displaystyle \frac{1}{2}\) 的機率,root 會切在最前面與最後面兩塊,這時 worst case 會是切在最邊邊的地方,高度只須高度看比較高的子樹,所以 \(H(n)=H(n-1)+1\)
換底公式 : \(\log_a n=\log_a b \times \log_b n\)
根據換底公式 : \(2\log_{\frac{4}{3}}n=2\times \log_{\frac{4}{3}}2\times \log_2 n\)
所以 \(H(n)=O(2\times \log_{\frac{4}{3}}2\times \log_2 n)=O(\log n)\)
故有 \(n\) 個點的 Treap 的高度高機率為 \(O(\log n)\)(失敗率 \(\displaystyle <\frac{1}{n^c}\))
基本操作¶
struct¶
變數¶
-
key:比較的依據,在中序1要由小到大
-
priority :維持 treap 形狀的依據,最大值在 root
-
val:要儲存的資料
-
lc, rc:左右子樹的 pointer
函式¶
-
push():把 root 的資訊傳遞給子樹(呼叫時放在要用到 lc, rc 之前)
-
pull():把子樹的資訊更新到 root
code
Merge¶
merge(a, b):把兩個 treap a, b 合併成一個 treap,用中序看 a 在左邊,b 在右邊
【前提】: 假設 a 的 key 都小於 b 的 key
code
Split¶
split(t, k):把 treap 按照 key 分成兩顆,第一顆的 key 都要小於等於 k
【前提】: 左邊 treap 的 key < 右邊 treap 的key
code
Split by size¶
splitBySize(t, k):把 treap 按照中序分成兩棵,第一棵的包含恰好 k 個 node,第二棵包含剩下的 n-k 個 node
【前提】: 左邊 treap 的 key < 右邊 treap 的 key
code
例題¶
CSES - Cut and Paste
給你一個長度為 \(n\) 的字母串,\(q\) 次將 \((l, r)\) 剪下貼到字母串的尾端,問最後的字母串
\(n,q\le 2\times 10^5\)
code
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 |
|
CSES - Substring Reversals
給你一個長度為 \(n\) 的字母串,\(q\) 次 reverse\((l, r)\),問最後的字母串
\(n,q\le 2\times 10^5\)
實作細節
注意 reverse 懶標再更改時是 xor
code
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 |
|
CSES - Reversals and Sums
給長度為 \(n\) 的陣列 \(a_1,\ldots, a_n\),\(q\) 次以下操作 :
-
\(\text{reverse}(l,r)\)
-
\(\text{sum}(l,r):\) 輸出 \(a_l+\ldots+a_r\)
\(n,q\le 2\times 10^5\)
實作細節
在 Node (int val) : val(val), pri(rand()), sum(val) {}
裡面要記得加 sum(val)
code
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 |
|
帶旋轉區間連續最大和
給長度為 \(n\) 的陣列 \(a_1,\ldots, a_n\),\(q\) 次以下操作 :
-
\(\text{reverse}(l,r)\)
-
\(\text{query}(l,r):\) 輸出 \(a_l,\ldots, a_r\) 的最大連續和
\(n,q\le 2\times 10^5\)
思路
在 pull 的時候就照線段樹那樣操作就好,只是在 push 的時候如下
Treap - rank tree LOJ #104. 普通平衡树
實作 Treap,維護支援以下功能:
- 插入 \(x\)
- 刪除 \(x\)
- 查詢 \(x\) 的是第幾小
- 查詢第 \(k\) 小的數
- 求小於 \(x\),最大的數
- 求大於 \(x\),最小的數
\(1 \leq n \leq 10^5,|x|\le 10^7\)
思路
可以寫一個 find_kth() 的 function,以方便查找(因為是 multiset 所以 SplitBySize 會壞掉,所以用 find_kth() 代替)
實作細節
在更動 cnt 時記得 sz 也要一起改變
code
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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
|
Treap - rank tree 數據加強版 LOJ #107. 维护全序集
維護一個 multiset,支援以下功能:
- 插入 \(x\)
- 刪除 \(x\)
- 查詢第 \(k\) 小的數
- 查詢有幾個數字小於 \(x\)
- 求小於 \(x\),最大的數
- 求大於 \(x\),最小的數
\(1\le n\le 3\times 10^5,0\le x\le 10^9\)
code
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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
|
POJ-3580 SuperMemo
給定一個長度為 \(n\) 的序列 A[]
,\(m\) 個以下操作:
-
ADD l r k
: 將A[l, r]
的每一項都加上k
-
REVERSE l r
: 將A[l, r]
反轉 -
REVOLVE l r k
: 將A[l, r]
右旋k
格 -
INSERT i x
: 將x
插入到A[i]
這一項的後面 -
DELETE i
: 刪除A[i]
這一項 -
MIN l r
: 輸出A[l, r]
中的最小值
\(n,m\le 10^6\)
實作細節
因為是 POJ 版本舊的關係要
也不能使用 auto,所以改用 pair<Node*, Node*>
還有 struct 裡面不能直接 assign 變數的預設值
還有 Node 不要維護多餘的資訊(例如 sum),不然會 TLE
code
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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
|
洛谷 P3834 - 【模板】可持久化线段树 2
給長度為 \(n\) 的序列,\(q\) 筆詢問
- \(\text{query(}a_l\sim a_r,k):\) 回答 \(a_l\sim a_r\) 中第 \(k\) 小的數值是多少
\(n,q\le 2\times 10^5,|a_i|\le 10^9\)
思路
離線解決,把區間排序好,區間移動時,把不用的元素刪掉,還沒加進 Treap 的元素加進去
TIOJ 1169. 氣球博覽會
有一個長度為 \(n\) 的陣列 \(a_1, \ldots ,a_n\),有以下 \(q\) 筆操作 :
-
單點修改
-
區間查詢不含某數的最長連續序列
\(n,q\le 2\times 10^5, a_i<2^{24}\)
思路
對於每種數字,開一個 Treap 紀錄出現的 index,Treap Node 裡維護 :
-
index
-
相鄰兩個距離的最大值
-
子樹最小 index
-
子樹最大 index
當查詢時就 Treap[c].SplitByIndex(l, r),將「相鄰兩個距離的最大值」與「l - 最小 index - 1」與「r - 最大 index - 1」取 max 就是答案
持久化 Treap¶
基本操作¶
通則 : 在 push 之前 copy 一份
struct¶
注意看 push() 函式裡面怎麼寫,尤其是更新順序
code
Merge¶
code
Split¶
code
Split by size¶
code
例題¶
持久化 Treap NPSC 2014 pD
給一個長度為 \(n\) 個子母串 \(s_1,\ldots ,s_n\),以及 \(m\) 筆操作 :
-
輸出 \(s_l,\ldots ,s_r\)
-
複製 \(s_l,\ldots ,s_r\),貼到原本 \(s_r\) 之後
-
reverse \(s_l,\ldots ,s_r\)
\(n,m\le 4\times 10^4\)
持久化 Treap - rank tree 洛谷 P3835 【模板】可持久化平衡树
實作持久化 Treap,支援以下功能:
- 插入 \(x\)
- 刪除 \(x\)
- 查詢 \(x\) 的是第幾小
- 查詢第 \(k\) 小的數
- 求小於 \(x\),最大的數
- 求大於 \(x\),最小的數
每一次操作都是基於某一個歷史版本,同時生成一個新的版本
\(1 \leq n \leq 5 \times 10^5,|x_i| \leq {10}^9\)
思路
在 Merge 和 Split 的時候,都一定要用 COW,不能跟一般沒 COW 的 Merge 跟 Split 混的用,不然會直接改動到好幾個版本的資料。COW 可以讓你先把以前的資料 copy 一份再使用
實作細節
我的話需要壓常才可以過
code
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 182 183 184 185 186 |
|
持久化 Treap 洛谷 P5055 【模板】可持久化文艺平衡树
來維護一個序列,其中需要提供以下操作 :
- 在第 \(i\) 個數後插入數字 \(x\)
- 刪除第 \(i\) 個數
- reverse 區間 \(a_l,\ldots ,a_r\)
- 輸出 \(a_l+\ldots +a_r\)
每一次操作都是基於某一個歷史版本,同時生成一個新的版本
\(1 \le n \le 2 \times {10}^5\),\(|x_i| < {10}^6\)
code
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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
|
-
中序 = 將 BST 裡面的元素從小到大輸出。因為一定是依序加入元素,index 也是從小到大加入,那麼你要將他以 index 小到大輸出,就是中序 ↩