题解 P4631 【[APIO2018] Circle selection 选圆圈】
Update on 2020.10.8:添加了一些参考链接。
希望看到这一篇帮忙顶一下,不然真就全网人类智慧 kdt 泛滥了。。。
有两个 复杂度正确 的解法哦!
代码详见 https://www.luogu.com.cn/paste/tlbnzyno
Description
给定平面上的
每次选取最大的一个尚未被删除的圆删除,并同时删除所有与其相切或相交的圆。
最后输出每个圆分别是被那个圆所删除的。
Hint
-
1\le n\le 3\times 10^5 -
0\le |x|, |y|, R \le 10^9
Solution 1
后来在 Codeforces 上找到的官方题解 Link here。如果对题解中某些说明无法理解可以参考上述内容。做法参考:Link here。
有一个非常简单的
我们尝试剪枝,缩小枚举的范围。
若当前最大圆的半径为
对于当前这个圆,我们只要搜索 其所在格子及其相邻的 即可(两个格子相邻定义为所在行的距离不超过 1,且列距离也不超过 1,换言之,一个格子所有相邻的就是周围一圈 8 个)。显然这样是不会漏记的,因为所有圆的半径都不超过方格大小,那么一定不会出现两个圆相交或相切,但却不在相邻两个方格内。
但我们总不能每次都搜怎么大的格子,最大圆的大小如果非常大而其他圆又很小的话这个剪枝没有丝毫用处。因此我们引入一个 重构机制:设现在考虑到的圆的半径为
重构的复杂度会不会有问题?观察到,当半径不足方格大小的一半时才会重构,重构之后如果又来一次那又得一半。于是整个算法不会有超过
可以证明每个圆被检查的次数为常数。因为对于一个圆,一个方格中不会有太多的大小相似的大圆对它进行检查,这些大圆会在之前就会被互相消除掉。
总复杂度为
但其实重构是可以做到一次
Solution 2
虽然也是有理有据的
对于一个圆
这些圆之间 不可能又有公共部分,否则它们之间会互相消除。
有一个比较显然的东西:这些圆一定在矩形
对于 set)显然可以搞。
然而发现这样只是求出一个圆的答案。现实相当于要多组询问,那么我们 CDQ 分治!
首先肯定是按
主要怎么做左侧对右侧的影响。首先对于左边的,我们取出所有 不是被带走的 圆的扫描线,作为“修改”;右侧则取出全部,作为“查询”。修改部分,形如
最后得到了一堆扫描线,然后按坐标大小的顺序去做。插入时将这个圆的
这样的复杂度是 然而被随机选择 KDT 吊起来打 QAQ
Solution 3
既然是“二维平面”,那么可以 KDT 暴力搞。
首先一个圆心为
那么考虑用 KDT 维护这些矩形。对于一个圆,搜索可能与之有交集的区域即可。
但这样复杂度是
于是发扬人类智慧,将所有的点 随机旋转 一个角度。于是就可以过 LOJ 数据了。
不过 Luogu 不需要旋转 qwq。
思维难度、代码难度和速度全方位碾压 Sol 1 & 2。
Code
https://www.luogu.com.cn/paste/tlbnzyno