题解:P17240 [IOI 2026] 弹球机 / ballmachine

· · 题解

大战了一天。

你首先考虑一个看上去特别弱智的问题:如何得知 n?很难不想到对每个叶子一直 insert 直到返回 false,对每个叶节点成功 insert 的次数加起来即是 n。但是这种操作能带给我们的只有 n 吗?

发现,我们同时还得知了按顺序加入 0 \sim m-1 的叶子,其往上跳直到跳到一个被访问过节点的链长。那么,我们只需要得知每一条链分别被挂在了先前已确定结构的哪个位置便能完整还原整棵树。

一个简单的想法是,给编号为 i 的叶子赋权 m-i-1,并用该值填满其所在的整条链,然后进行一次 collect,这样在 dfs 时如果遇到了叶子编号比自己靠后的链便会先访问它,这样就可以通过嵌套结构来识别链头挂的点是哪个。可以做到的代价为 m,得分 47

思考一下这个做法为什么不牛?我们共有 m 条链,如果我们需要给 m 条链分配两两不同的权值,那所需的值域也会达到 m。有什么办法在保证每条链有一个独有的标记的情况下还能够保证递归结构吗?

思考一下,我们需要保证递归结构,只需要让每次在主链上递归时如果有副链的儿子,优先递归它就行了。所以链头和其余部分是可以使用不同的权值的,不妨使用一个二元组 (x,y) 来编号一条链,表示我们将该链链头的权值设为 x,其余部分设为 y。由于需要满足前面所述的性质,需要保证任意一条链的 x 值均小于任意一条链的 y

不难发现此时我们只需要 2\sqrt m 的值域就能编号所有链了。递归还原时,先递归处理掉所有挂在链头上的链,便可以得知该链所编号的二元组。之后遍历链上每个点,继续递归还原所有挂在该点上的链即可。

但是我们真的能成功编号所有链吗?使用二元组进行编号,需要满足链长 \ge 2,而我们还可能有若干条链长 =1 的链,怎么办呢?

沿用我们之前的方法,假设有 k 条长度为 1 的链,我们将这若干条链编号为最小的 0 \sim k-1,其余二元组编号往后平移,并在递归复原结构时加一步提前判掉这些长度为 1 的链即可。但是这样值域最劣又会来到 k

故技重施一下,将这些链分为 \sqrt{k} 组,对每组内按上述方法赋值,共还原 \sqrt{k} 次即可。总的值域是 \sqrt{k}+2\sqrt{m-k},collect 次数是 \sqrt{k}+1,最坏情况是取 k=\dfrac{m}{2},代价为 41,可以通过。