UOJ #791. 【CTS2023WC2023】樹據結構

275307894a發表於2024-12-09

題面傳送門

很高妙的互動題。以下均預設已經進行了 \(n\) 次操作將值隨機排列。

首先考慮一條鏈怎麼做。有一種類似快速排序的方法:隨機一個排列,每加入一個位置,就以這個位置為分割線將值分成兩半,這可以從小到大列舉值,然後和最大值交換實現。這樣的操作次數是這個點和前面最近的加入點的距離。期望是 \(O(n\log n)\) 的。

然後還有另一種做法:詢問每個位置的值,按照這個值將鏈分成若干塊。每次取出最小的塊,用最小值和這個字首最大值交換,會繼續分裂成若干塊。這個的詢問次數相當於每個位置的單調棧長度之和,也是 \(O(n\log n)\) 的。

對於不是鏈的情況,上述兩種演算法均會讓這棵樹變成:每個點到父親的邊是這個點到根路徑上最大的邊。這時我們按照這個值從小到大排序然後依次加入,從權值為 \(1\) 的邊開始,先詢問這個點到根路徑上的最大值,然後將 \(1\)\(i+1\) 交換,這樣能保證詢問到第 \(i\) 個點的時候這個點到父親的邊權值是 \(1\),並且之前加入的點到父親的權值分別為 \(2\)\(i\),這樣一次詢問就能找到這個點的父親了。這部分需要 \(n\) 次詢問和修改。

然後我們繼續考慮怎麼將鏈做到 \(O(n)\)。考慮倍增,每次加入前 \(2^B\) 大的邊和隨機出來的前 \(2^B\) 個點。具體的,假設現在我們已經將前 \(2^{B-1}\) 大的邊和隨機出來的前 \(2^{B-1}\) 個點排好了,然後我們先加入之後 \(2^{B-1}\) 大的邊,然後加入後 \(2^{B-1}\) 個點,加入的過程均按照鏈的第一種做法維護值域連續段,這樣期望只需要 \(O(n)\) 次。這個做法啟發的地方是在於發現,如果已經知道若干個點在鏈上的順序,則只需要 \(n\) 步而非 \(n\log n\) 步操作就可以按照這些點將值域劃分開來。所以在 \(2^{B-1}\to 2^{B}\) 的過程中,先利用已經知道順序的 \(2^{B-1}\) 個點劃分序列,之後插入的 \(2^{B-1}\) 個點到之前已經選過的點的期望距離就是 \(O(1)\) 的。

對於樹只需要執行鏈這個做法然後類似用 \(n\) 步操作求出每個點父親即可。在 \(n=5\times 10^5\) 時大約需要 \(3\times 10^6\) 步。

submission

相關文章