这一题的一个比较简单的思路,就是对于一个点 $a_i$,找到 $y$ 坐标在区间 $[y_i + m, max_y]$ 内的所有点中,$x_i$ 的前驱和后继,然后更新答案。 对于二维平面上的的区间查询问题,树套树应该是最经典的解法了。比如这一题可以用线段树套平衡树,对于线段树的每个节点 $[L, R]$ 上建立一个平衡树,保存输入序列上 $y$坐标 落在 $[L, R]$ 内的点的 $x$ 坐标。因为平衡树本身支持查询前驱和后继,所以实现树套树之后就很简单了,该算法的时间复杂度为 $O(n \log^2 n)$。不过似乎树套树主要用来解决带修的这种问题,并且不容易调试,码长略长,所以还是思考另外的做法。
跟磁力块那一题的解法类似,可以用分块做,一开始觉得用分块时间复杂度可能比较高(当时还准备放弃写树套树,幸亏我太蒻了没学过树套树,要不然掉进坑里了),但是实际好像相当快? 对于每个块用 $y$ 值排序,内部用 $x$ 值排序,对于每一个点,二分找到满足条件(即 $y$ 坐标和该点的 $y$ 坐标的差 $\ge m$)的最小的块(预先保存每个块的最小 $y$ 值),由于预先排序保证了这个块之后的块也都满足条件,然后再在这些块内部二分找到 $x$ 坐标的前驱和后继,更新答案,然后对于最左的块的左边一个块,朴素枚举,更新答案。 对于每一个点的查询操作,时间复杂度为 $O(\sqrt n \log n)$ 故总时间复杂度为 $O(n \sqrt n \log n)$
当然,这样的时间复杂度还是很高,但是还有很多优秀的算法:
譬如 skylake 大佬的算法:通过维护 $y$ 坐标的区间最值,然后用尺取法,保证两个最值的差 $\ge m$,然后更新答案。这个算法实在是太优秀了,并且代码写出来极为简洁,好像是大佬开始比赛十分钟秒掉的?简直叹为观止 orzorz%%%%%%%%,我还没有学过 RMQ,可能不是很了解,不过据推测这个时间复杂度是 $O(n \log n + n)$?果然犇人写犇算法,直接比我的算法快一个数量级(虽然可能这一题数据太水,我的代码反而快了 再譬如 关神犇 提出的算法:维护两个二叉堆,堆内以 $y$ 坐标为关键字,并且在堆外以 $x$ 为关键字排序。每次枚举一个点就取出两个堆中满足条件的点,更新答案,然后弹出这些数(因为已经以 $x$ 为关键字排序,所以在当前点之后的点不会离这些点更近,即这些点对于答案已经没有贡献了),之后把该点存入堆内。这个算法实在是太神了(关神犇您是我的神orzorzorz%%%%%%%%%%%%%%%),因为只进行了 $2n$ 次插入和 $n$ 次删除操作,每个操作的时间复杂度是 $\log n$,所以总时间复杂度为 $O(n \log n)$。看这道题的标签里有优先队列,也许正解就是这个,为此再次膜拜神犇叹为观止的做题直觉orzorz。 当然,除此之外,还有用线段树维护区间最值的神犇的方法,具体思路和 skylake 大佬的大同小异,不过线段树实现代码更加冗长,这里不再赘述。 上述各位神犇的思路我都进行了代码实现,可以去 我的博客 察看作为参考。
2022-09-16 23:38:30
|