資料結構-黃洛天
A - 冰火戰士
題面
支援$Q$次兩種操作,
- 新增一個三元組 $(w,a,b),w\in{0,1}$
- 撤回第 $k$ 此操作,此操作保證為報名資訊
每次操作後,求
$$
\max_{x}\min(\sum_{w_i=0,a_i\le x}b_i,\sum_{w_i=1,a_i\ge x}b_i)
$$
以及取到最值的最大的 $x$。
$1\le Q\le 1\times 10^6,1\le x\le 2\times 10^9,不存在相同的三元組$
題解
有一個很顯然的$\mathcal O(n\log^2n)$ 做法,即對每個 $w\in{0,1}$ 開一個樹狀陣列,每次二分查詢 $x$,並判定 $\sum_{w_i=0,a_i\ge x}b_i-\sum_{w_i=1,a_i\le x}b_i$ 的正負,使其最接近 $0$,確定了值之後,再倍增求得 $x$ 。
考慮最佳化,我們發現,樹狀陣列上的 $p$ 號節點儲存的恰為 $[p-\operatorname{lowbit}(p)+1,p]$,利用這一點,我們可以在倍增的過程中不斷同步累加樹狀陣列中的值,從而最佳化掉一個 $\log$。
當從 $p$ 倍增到 $p+2^i$ 次方時,由於$\operatorname {lowbit}(p)>2^i$ ,所以我們只需要加上 $bitr[p+2^i]$ 檢視是否合法即可。
複雜度 $\mathcal O(n\log n)$。
方法
- 倍增同時 $O(1)$ 查詢樹狀陣列中的值。
程式碼
連結
B - Fenwick Tree
題面
有一個長度為 $n$ 的樹狀陣列,給出在進行了若干次單調修改操作後每個位置儲存的值是否非 $0$,求最少進行了幾次修改操作。
$1\le n\le 10^5$
題解
每次修改相當於給某個點到根加一個數字。
我們從低向上考慮整個樹,依次判斷是否要給當前節點操作。假設他有 $k$ 個兒子是大於 $0$ 的。
-
$k = 0$,子樹內所有操作對於我的影響是 $0$.
-
$k = 1$,子樹內所有操作對於我的影響一定非 0
-
$k > 1$,子樹內所有操作對於我的影響是任意的。
因此只有 $k = 0$ 且目標是 $1$ 的時候需和 $k = 1$ 且目標是 $0$ 的時候要進行操作。 時間複雜度 $\mathcal O(n)$。
方法
-
考慮貢獻
整體考慮子樹對父親的貢獻。
C - Traveling in Cells
題面
有 $n$ 個二元組 $(c,v)$ ,支援 $q$ 次三種操作
-
$1\ p\ x$:將 $c_p$ 修改為 $x$
-
$2\ p\ x$:將 $v_p$ 修改為 $x$
-
$3\ p\ k\ a_{1\sim k}$:求 $\max\limits_{p\in [l,r]}\sum\limits_{\forall i\in[l,r],c_i\in{a_1,a_2,\cdots,a_k}}v_i$
$1\le n\le 10^5,1\le q\le 10^5,\sum k\le 10^6$
題解
對於每一個 $c$ 建立一顆線段樹維護區間個數,前兩個操作就是單點修改,第三個操作結合樹狀陣列求解。
對於第三個操作,本質上是找到左與右第一個的沒有出現在 $a$ 中的 $c$ ,只需要看這些顏色是否鋪滿一個區間便是,程式碼如下:
int RP(vector<int> u,int pos,int L=1,int R=n){
if(Cnt(u)==R-L+1)return n+1;
if(L==R)return L;
int mid=L+R>>1;
if(pos<=mid){
int res=RP(lson(u),pos,L,mid);
return res==n+1?RP(rson(u),pos,mid+1,R):res;
}else{
return RP(rson(u),pos,mid+1,R);
}
}
int LP(vector<int> u,int pos,int L=1,int R=n){
if(Cnt(u)==R-L+1)return 0;
if(L==R)return L;
int mid=L+R>>1;
if(pos>mid){
int res=LP(rson(u),pos,mid+1,R);
return res==0?LP(lson(u),pos,L,mid):res;
}else{
return LP(lson(u),pos,L,mid);
}
}
方法
- 最近元素查詢模型——線段樹左右第一個查詢法,程式碼與上類似
D - k-Maximum Subsequence Sum
題面
長度為 $n$ 的數列,支援兩種操作:
-
修改某個位置的值。
-
詢問區間 $[l,r]$ 裡選出至多 $k$ 個不相交的子段和的最大值。
一共有 $m$ 個操作。
題解
要求 $k$ 個不交的,那麼可以先求出最大子段和,假設區間為 $[l, r]$,然後給區間 $[l, r]$ 內的所有元素取相反數,也就代表著此後你要是不想選這個數,那麼就取一遍相反數抵消掉,以此類推做 $k$ 輪。
線段樹需要維護:區間和,字首和最大值,字尾和最大值,區間和最大值,字首和最小值,字尾和最小值,區間和最小值,以及每種最大最小值取到的方案。
時間複雜度 $\mathcal O(n \log n)$。
方法
- 線段樹結合貪心
程式碼
連結
E - Max Mex
題面
給定一棵有 $n$ 個點的樹,每個節點有點權。所有的點權構成了一個 $0\simn - 1$ 的排列。有 $q$ 次操作,每次操作 $1$ 為交換兩個點的點權,操作 $2$ 為查詢 $Mex(l)$ 值最大的 $Mex(l)$ 值,其中 $l$ 是樹上的一條路徑。定義一條路徑 $l$ 的 $Mex$ 值 $Mex(l)$ 為這條路徑上最小的沒有出現過的自然數
$2\le n\le 2\times 10^5,1\le q\le 2\times 10^5$
題解
考慮使用線段樹維護維護值域,也就是對於值域區間 $[l, r]$ 的點,他們在樹上的位置能否組成一條鏈,如果可以這個鏈的端點是誰。合併的時候分類討論。
查詢時記錄當前維護值域之前的資訊,進行合併
方法
-
線段樹維護其他資訊
線段樹不一定維護數值資訊,只要是有結合律的操作都可以,比如本題的合併。
程式碼
連結
F - The Fair Nut's getting crazy
題面
給定序列 ${\alpha}$,求有多少個四元組 $(a,b,c,d)$ 滿足以下條件
-
$a<c\le b<d$
-
$\forall i\in[c,b],\forall j\in[a,d],\exist!i=j,\alpha_i=\alpha_j$
解法
令 $pre_i=\max\limits_{\alpha_j=\alpha_i,j<i}j,nxt_i=\max\limits_{\alpha_j=\alpha_i,j>i}j$
則對於一個合法四元組 $(a,b,c,d)$,有 $\max\limits_{i=c}^bpre_i<a<c\le b<d<\min\limits_{i=c}^bnxt_i,$
易得最遠情況下, $a=\max\limits_{i=c}bpre_i+1,d=\min\limits_{i=c}bnxt_i-1$
所以 $\max\limits_{i=c}^bpre_i+1<c\le b<\min\limits_{i=c}^bnxt_i-1$
這關乎兩個變數,所以假設我們列舉 $c$ 則答案 $b$ 的選取具有單調性
當 $b$ 去到最遠時,答案為 $\sum\limits_{i=c}b(c-\max\limits_{j=c}ipre_j-1)(\min\limits_{j=c}^inxt_j-1-i)$
由於計算的下邊界都是 $c$ 考慮倒序列舉,動態維護上式
展開得:$\sum\limits_{i=c}^b (c-1) * \min\limits_{j=l}^inxt_i - (c-1)*(i+1) - \max\limits_{j=c}^ipre_j * \min\limits_{j=c}^inxt_j + \max\limits_{j=c}^ipre_j * (i+1)$
用線段樹分別維護即可
抽象化方法
-
先數學化,再推導
-
多變數關聯情況——列舉+維護
如上式中會出現一個同時與 $b,c$ 相關的式子,我們可以考慮列舉一個,維護一個
-
維護法——拆式子
將式子拆開,分別維護每一部分,要比維護整體更簡單
G - Useful Algorithm
題面
定義 $val(a,b)=\max\limits_{{i|a+b在二進位制的第i為下進位}}w_i$,例如 $\because 5+3=101_2+11_2=1000_2,所以val(5,3)=\max(w_1,w_2,w_3)$
給定長度為 $n$ 的序列 ${c}$ 和 ${d}$,求$\max\limits_{i,j}val(i,j)\times(d_i+d_j)$,並支援單點修改 $c,d$
$1\le m\le 16,1\le c_i\le 2^m,1\le n\le 10^5,1\le d\le 10^9$,強制線上
解法
$$
\begin{aligned}
&\max\limits_{a,b}\max\limits_{{i|a+b在二進位制的第 i 位進位}}w_i\times(d_a+d_b)
\=&\max\limits_{i}w_i\max\limits_{{a,b|a+b在二進位制的第 i 位進位}}d_a+d_b
\&令A=a\bmod 2i,B=b\bmod2i,f_A=\max_{i\bmod 2^i=A} d_i
\=&\max_iw_i\max_{A+B\ge 2^i}f_A+f_B
\&令g_A=f_{2^i-A}
\=&\max_iw_i\max_{A\le B}g_A+f_B
\end{aligned}
$$
對於每一位維護一棵線段樹即可
抽象化方法
-
$a+b\ \ \ \ p$ 進位制下第$i$ 位進位是指 $a\bmod p^i+b\bmod p^i\ge p^i$
-
拆式子——交換內外迴圈
本題中將外迴圈變為每一位,內迴圈變為數,以簡化處理
-
形如 $a\cdot b\ge C的卷積$,可以化為 $C\cdot \overline{b}\le a$,用線段樹進行維護
二元運算 $\cdot$ 可以為 $\oplus+\times$等存在逆元的運算
H - Vacation
題面
給定序列 $a_{1\sim n}$, $m$ 個操作,與一個限制 $c$ 。
單點修改,查詢區間中長度不超過 $c$ 的最大子段和。
$1\le c\le n\le 2\times 10^5,1\le m\le 5\times 105,-109\le a_i\le 10^9$
題解
參考題解:GYM103861F 解題報告
先將序列按照長度 $c$ 分塊。
最終答案僅可能來自於兩種情況
-
屬於一個塊
-
跨過至兩塊
對於第一種情況,我們直接維護一個普通的最大子段和。
對於第二種情況:答案一定由一個字尾加上一個字首組成。令 $l,r$ 表示左右端點在其各自段中的編號,易得 $l\ge r$ 。所以我們去維護 $\max pre_x,\max suf,\max_{x>y}suf_x+pre_x$ 就行了(其中 $pre$ 表示這一段的字首,$suf$ 表示前一段的字尾)。
所以說,我們需要維護三種線段樹
-
維護最大欄位和
-
對於每一塊,維護與前一塊 $pre,suf$ 拼接情況
-
對於全域性,維護前兩種線段樹在考慮完整段時的答案
下面是具體操作:
修改
修改整棵最大子段和線段樹的答案並更新維護答案的線段樹
對前面塊的字尾和進行字首加並更新維護答案的線段樹
對後面塊的字首和進行字尾加並更新維護答案的線段樹
查詢
令 $L,R$ 表示左右端點 $k_l,k_r$ 為左右端點所在的塊, $l,r$ 分別為左右端點的塊內編號。
-
當 $R-L+1\le c$ 直接詢問最大子段和
-
當 $(R-L+1>c)\land(k_r=k_l+1)$,此時將一個塊分為了三個部分 $[1,l),[l,r],(r,c]$,答案來自於三種情況。
-
$\max_{l\le y<x\le c}suf_x+pre_y$
-
$\max_{x\in [1,l)}pre_x+\max_{x\in [l,c]}suf_x$
-
$\max_{x\in [1,r]}pre_x+\max_{x\in(r,c]}suf_x$
取最大值即可。
-
-
當 $(R-l+1>c)\land(k_r>k_l+1)$,答案來自於兩種情況
-
中間部分,即 $k_l+1\sim k_r-1$ 中的整段最大子段和同 $k_l+1與k_l+2,\cdots,k_r-2與k_r-1$ 中的完整跨段關係。
-
兩邊部分,轉化為兩個情況二。
-
時間複雜度 $\mathcal O((n+m)\log n)$
方法
-
分段維護線段樹
-
將兩端之間有關聯的資訊放入一個線段樹進行處理。
J - Game: Celeste
題面
數軸上有 $n$ 個點,第 $i$ 個點的座標為 $x_i$,權值為 $a_i$。小 A 可以從 $x$ 跳到 $[x+L,x+R]$ 的一個位置,跳到一個位置會拾取當前位置上權值,他想從第 $1$ 個點跳到第 $n$ 個點,並使得拾取權值的集合單調不增排列後的字典序最大。若不能到達,輸出 $-1$。
$\forall i\in[1,n-1],x_i<x_{i+1}<10^9,1\le a_i\le n\le 10^5,1\le L,R\le 10^9$
解法
用線段樹維護所選集合。考慮單調佇列最佳化 DP。
按照字典序每次選出最“大”的集合,轉移就是以此版本為基礎建立可持久化線段樹。
比較集合大小就用線段樹上二分,如果 $root[n]=0$,則不能到達。
抽象化方法
-
維護集合——權值線段樹
-
可持久化資料結構——一種轉移關係
所以可以用來DP
-
資料結構比大小,定義其大小關係,以便用在$\min,\max$ 等情境中。
K - 道路建設
題面
給定平面上 $n$ 個點的座標,求前 $k$ 小的點曼哈頓距離之和。
$2\le n\le 2.5\times 10^5,1\le k\le \min(2.5\times 10^5,\frac {n(n-1)}{2})$
題解
為了方便先離散化,把 $x, y$ 均離散化成不同的。
因為 $k$ 比較小,考慮對於每個點求出右側距離他最近的點,然後把這些距離扔到一個堆裡,每次取出最小值。最小值對應的點為 $u$。把距離 $u$ 第二近的點的距離和 $u$ 扔到堆裡。
因為我們限定了只考慮橫座標在一個點 $(x',y')$ 右邊的點 $(x,y)$。
-
當 $y>y'$ 時,$|x-x'|+|y-y'|=(x+y)-(x'+y')$
-
當 $y<y'$ 時,$|x-x'|+|y-y'|=(x-y)-(x'-y')$
所以查詢距離 $(x',y')$ 最近的點只需要查詢 $[1,y')$ 裡的 $x-y$ 的最小值和 $(y'n]$ 內 $x+y$ 的最小值
考慮從右到左掃描線,用主席樹維護區間 $y + x$ 的最小值和 $x − y$ 的最小值,每次加入一個點。
如果要找第二小值,就把第一小值對應的 $y$ 的地方設為正無窮即可。注意為了不影響其他點的線段樹,這裡也需要可持久化(即新建節點)。
方法
-
離散化
在離散化時去重就是相同對相同,在離散化的時候不去重並加上標記就是相同對不同,這樣離散為不同有助於許多情況的簡化。
-
可持久化的修改
新建節點——再次可持久化,以避免對後續版本造成影響。
-
掃描線
從一個方向至另一個方向的動態線段樹資訊就是掃描線,這樣確定了一個列舉(處理)順序,方便處理
L - 樹
題面
給定一棵 $n$ 個結點的有根樹 $T$,結點從 $1$ 開始編號,根結點為 $1$ 號結點,每個結點有一個正整數權值 $v_i$。
設 $x$ 號結點的子樹內(包含 $x$ 自身)的所有結點編號為 $c_1,c_2,\dots,c_k$,定義 $x$ 的價值為:
$
val(x)=(v_{c_1}+d(c_1,x)) \oplus (v_{c_2}+d(c_2,x)) \oplus \cdots \oplus (v_{c_k}+d(c_k, x))
$
其中 $d(x,y)$ 表示樹上 $x$ 號結點與 $y$ 號結點間唯一簡單路徑所包含的邊數,$d(x, x) = 0$。$\oplus$ 表示異或運算。
請你求出 $\sum\limits_{i=1}^n val(i)$ 的結果。
$1\le n,v_i\le 525010$
題解
將 $(v_{c_i}+d(c_i,x))$ 看作一個數,我們發現這實質上是要支援三種操作——合併子樹資訊、全域性加一與插入一個值,維護一種資訊——全域性異或和。由於是二進位制資訊,這裡考慮0/1字典樹,合併子樹資訊與插入一個值都很簡單。
考慮如何全域性加一。
我們發現對一個數加一在二進位制下的步驟可以解釋為,找到最低為的 $0$ 將其賦為 $1$ 並將那些位數比它低的 $1$ 複製為 $0$。
回到這顆0/1樹上,可以解釋為先交換一個節點兩條邊下面的兩棵子樹,再向交換後的 $0$ 邊進行遞迴操作,這樣一次複雜度為 $\mathcal O(\log v)$
考慮維護異或值,我們可以考慮每條邊的貢獻。如果一條 $1$ 邊的子樹中有奇數個值時,累計這個一,否則不累計,每次維護 $\mathcal O(1)$。
時間複雜度 $\mathcal O(n\log v)$
方法
-
01-trie 可以用來維護一些數字的異或,支援修改
其插入、刪除、全域性加一、全域性減一(從遞迴 $0$ 邊變為遞迴 $1$ 邊)、合併操作都十分常見。注意
M - 眾數
題面
定義眾數為序列中出現次數嚴格大於一半的數字。
一開始給定 $n$ 個正整數序列,編號為 $1 \sim n$,初始序列可以為空。
有 $q$ 次操作,操作有以下型別:
- $1 \ x \ y$:在 $x$ 號序列末尾插入數字 $y$。
- $2 \ x$:刪除 $x$ 號序列末尾的數字。
- $3 \ m \ x_1 \ x_2 \ x_m$:求 $x_1, x_2, \ldots, x_m$ 合併後的眾數。不存在返回 $-1$。詢問中的合併操作不會對後續操作產生影響。
- $4 \ x_1 \ x_2 \ x_3$:新建一個編號為 $x_3$ 的序列,其為 $x_1$ 號序列後順次新增 $x_2$ 號序列中數字得到的結果,然後刪除 $x_1, x_2$ 對應的序列。
$q,n\le 5\times 10^5,\sum m\le 5\times 10^5$
題解
方法
- 主席樹+連結串列
N - 樓房重建
單側遞迴線段樹
題面
維護一個序列,線上單點修改,求出每一時刻各段的字首最大值數目。
$1\le n,m\le 10^5$
題解
既然是線段樹,首先考慮如何合併資訊。
如圖,黑線兩側是一個節點的左右子樹,很明顯左子樹的答案我們是一定要保留的,對於右子樹,我們應該保留那些比黃色的線更高的節點,如果直接用可持久化平衡樹維護每個子樹中的所有點,一次去 $\mathcal O(\log)$的查詢自然是可以的,但這樣太麻煩。
我們知道,在有序的序列上二分查詢比一個數小的複雜度是 $\mathcal O(\log)$ 的,我們可以用同樣的原理,圖中紅線是將右兒子再次分割,我們可以發現,要麼一個孫子全部在黃線上,要麼另一個孫子全部在黃線下,我們都只需要遞迴查詢另一個。
這個問題也就是一次上傳是 $\mathcal O(\log)$ 的,這樣總複雜度 $\mathcal O(n\log^2n)$
方法
-
線段樹維護單調序列
也就是本題的一個套路
-
線段樹的
Up
可以看作一個從來是線段樹的重中之重,在分析時,我們可以將其看作一個新的問題,這個問題又可以有多種解法,比如說這題,查詢黃線上的數,就可以線上段樹上再次遞迴實現,不僅如此,我們可以將其他資料結構、演算法等知識運用其上,解決問題。 -
本題也可以使用 T 題的方法達到 $\mathcal O(n\log n)$
P - Rikka with Data Structures
題面
給定序列 $a_{1\sim n}$
有 $m$ 次三種型別的操作:
- 區間加
- 區間賦值
- 給定$l\ r\ x$:計算滿足 $\max\limits_{i=\min(x,y)}^{\max(x,y)}a_i=max{A_x,A_y}$ 的 $y$ 的數量。
$多測,1\le T\le 200,1\le n,m\le 10^5,\sum n,\sum m\le 10^6$
題解
可以注意到 $y$ 的位置是由若干比 x 小的位置,及第一個比它大的位置後面的字首和最大值構成的,那個位置可以用“最近元素查詢模型”找,後面那一段用單側遞迴線段樹處理。
複雜度小常數 $\mathcal O(n\log^2n)$,最近元素查詢模型換成 $\log^2$ 的二分加線段樹會超時。
方法
-
最近元素查詢模型
-
單側遞迴線段樹
S - Bear and Bad Powers of 42
題面
定義一個正整數是壞的,當且僅當它是 $42$ 的次冪,否則它是好的。
給定一個長度為 $n$ 的序列 $a_i$,保證初始時所有數都是好的。
有 $q$ 次操作,每次操作有三種可能:
1 i
查詢 $a_i$。2 l r x
將 $a_{l\dots r}$ 賦值為一個好的數 $x$。3 l r x
將 $a_{l \dots r}$ 都加上 $x$,重複這一過程直到所有數都變好。
$n,q \le 10^5$,$a_i,x \le 10^9$。
題解
T - 前進四
題面
單點修改,詢問 $a_x,⋯,a_n$ 的不同的字尾最小值個數。
$1\le n\le 10^6$
題解
很明顯有一個$\mathcal O(n\log^2n)$ 的單側遞迴線段樹做法。但這道題會超時。
如果我們把時間當作空間的一維,那麼一個序列上的操作其實是在二維平面上的操作,如圖:
我們在時間軸上進行掃描線,單點修改,維護字首資訊。
而這道題,我們發現查詢是一個單點修改,維護字尾資訊,然而修改卻只在一個時間段有效,所以對於這道題,可以考慮將其轉過來。
變成一個區間賦最小值,字尾序列掃描線。
方法
- 考慮時間維度