把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

UOJ #791. 【CTS2023WC2023】树据结构

题面传送门

很高妙的交互题。以下均默认已经进行了 \(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

posted @ 2024-12-09 22:26  275307894a  阅读(20)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end