IOI 2021 钥匙 题解

First Post:

Last Update:

以前是想到过类似的算法的,但这题并没有突破成功,后来发现做法改一改就好了。很好的图论题。

先考虑 怎么做,就是枚举每个点,维护其能到达的连通块,用个队列进行广搜。对每种颜色维护一个 vector,对于在连通块边界上但暂时还走不过去的点,将其加入对应颜色的 vector 中。广搜时将队首出队后,先把所有该点对应颜色 vector 中的点入队后,再去看它的相邻点。这样我们就可以求出每个点能到达几个点,单次时间复杂度

上述过程主要输在各个点之间的信息没有互相利用,对每个点的求解完全独立。况且我们只需要知道 最小的位置,并不需要求出每个 。于是考虑有什么信息能让我们在 之间比大小。

对于一个点 ,如果其没有颜色为 的邻边,显然其 ,否则。如果 之间的边颜色为 。那 肯定大于等于

于是就有一个想法,建一张新图,对于一条边 ,如果 就在新图中连一条有向边 。连完之后的无出边点用来更新答案。然后把 SCC 缩成一个新点,重新建边。此时每个点对应原图中一个连通块,其大小就不一定是 ,其中包含的钥匙也不一定只有一种,把上文的 改成 即可。一直重复上述过程。

但这种做法最坏需要重复 轮,跟原来的暴力没区别。

这一定是我们信息用的不够完全。注意到上述做法没有充分利用一条边的比大小功能。究其原因是我们对”连通块“ 的限制太严格,要求其一定得是 SCC,导致连通块的合并机会其实很少。考虑拓展连通块的定义,我们只需要其中存在一个点 ,使得块中所有点都能到达 即可。 不一定要能到达连通块中的所有点。

此时原图仍然被分成若干个连通块,每个连通块都有一个代表点 。考虑仍然通过”找出边“ 的方式合并这些连通块。对于一个连通块,从其对应的 开始搜索,直到走到一个不在当前块中的点 。那么我们可以就可以把 中的所有点合并到 上。如果不存在这样的点,那么 设 BFS 过程中所走到的点集为 ,容易发现 中每个点可以到达的点集都恰好是 。用 更新答案即可。同样执行若干轮这种合并过程,就能得到最终解。

时间复杂度方面。因为每个连通块每轮要么没有出边,此时其会被删去,后面不考虑它,要么有条出边,能够将两个连通块合并起来,所以合并后连通块个数至少减少一半,因此总共只会执行 轮合并过程,每轮每个连通块 BFS 的复杂度之和是 的。总时间复杂度为

代码:https://loj.ac/s/2003753