持久化
相關知識 :
CSES - Range Queries and Copies
有一個長度 \(n\) 陣列,\(q\) 筆以下操作:
-
對於第 \(k\) 個陣列單點改值
-
對於第 \(k\) 個陣列區間求和
-
複製第 \(k\) 個陣列,並將其添加到列表的尾端
\(n,q\le 2\times 10^5,a_i\le 10^9\)
code
洛谷 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\)
思路
對於每個 \(i\) 維護陣列 \(v_i\),對於每個 \(v_i\) 先等於 \(v_{i-1}\),然後再將 \(v_i[a_i]\)++
這樣問一個區間的時候可將 \(v_r-v_{l-1}\) 得到一個陣列,在上面 walk
實作上因為 0-base 的時候 \(l-1\) 會是負的,所以我們將 \(v_i\) 的 index 向右平移(詳見代碼)
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 |
|
CSES - Distinct Values Queries
給你長度為 \(n\) 的陣列 \(a_1,...,a_n\),有 \(q\) 個查詢
- 輸出 \(a_i,...,a_j\) 之間有幾種不同的數字
\(n,q\le 2\times 10^5\)
思路
跟離線算法的套路差不多。按照 \(r_i\) 小到大枚舉,維護每個數字最後出現的位置放 \(1\),其他放 \(0\)。這個可以對每個 \(r_i\) 使用持久化線段樹單點改值做到,詢問的時候只要去對應的 \(\texttt{roots}[r_i]\) 詢問區間和即可
實作細節
壓常
normal 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 |
|
AC 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 |
|
SPOJ COT
給定一顆 \(n\) 個點的樹,每個點都有一個編號。現在要求在線回答 \(q\) 筆詢問 :
- \(\text{query}(u, v, k):\) 輸出 \(u\) 到 \(v\) 之間的路徑上第 \(k\) 小的點編號。
\(n,q\le 10^5\)
思路
我們為每個頂點維護一個值域線段樹,記錄從根到這個頂點的路徑上每個編號的出現次數。可以發現如果我們這裡借助持久化技術,可以保證時空複雜度為 \(O(n \log n)\)。
之後對於每個請求,記 \(rt=\text{lca}(u,v)\) ,\(par_{rt}\) 為 \(rt\) 在樹上的父親。那麼二者路徑上的統計關係可以通過 \(u+v−rt−par_{rt}\)(這裡表示的線段樹的加減法)來獲得。我們不需要再新蓋一顆線段樹,只需讓他們加減然後在上面二分即可,這樣是 \(O(\log n)\)
總的時間複雜度為 \(O((n+q)\log n)\)
NPSC 2020 高中組決賽 pE. 排列
給一個 \(1\) 到 \(n\) 的排列 \(p_0\),還有 \(m-1\) 個排列,第 \(i\) 個排列是把上一個排列的第 \(x_i\) 和 \(y_i\) 項交換,求字典序排序後的結果(輸出編號)。
\(2\le n\le 10^5, 1\le m\le 10^5\)
思路
樣把每一個排列當成時間,交換當成兩個單點修改,然後弄一棵維護前綴 hash 的持久化線段樹,第 \(i\) 個版本就是第 \(i\) 個排列,比較字串時,在線段樹上二分搜兩個排列的最長共同前綴,這樣就可以用 \(O(\log n)\) 的時間比較排列,總時間複雜度會是 \(O(n\log^2 n)\)。