P4155、P6902 —— 一类区间覆盖问题
最近在写题的时候刷到两道思路较为相似的题(经验题),大体都是倍增+贪心,于是蒟蒻挑着时间来总结一下。
P4155 国旗计划
题意
借用一下学长讲课时的题目介绍。
一个 \(m\) 个点的环,有 \(n\) 条线段,可覆盖在环上 \([c_i,d_i]\) 的部分,保证线段之间不会互相包含,对 \(1\le i\le n\)问强制选择线段 \(i\) 时,最少选择多少条线段可以覆盖整个环。
数据范围:\(n\le 2\times 10^5,m<10^9\)。
思路
首先,对于环问题,我们可以套路的考虑将原来的环化成二倍长度的链,这样题目就能够转化成区间覆盖问题了。
考虑当选择了一条线段 \(i\) 后,对于下一个加入的线段有哪些限制。假设线段 \(i\) 的右端点为 \(r_i\),那么下一个线段必须要满足:
当前线段要能够与下一个线段覆盖的区间有交集,即 \(r_i\ge l_j\)。
满足如上条件后,才能保证我们可以选这条线段,但是可能有很多条线段可供选择,到底选哪一条呢?
不难看出,我们贪心的选择合法的所有线段中,能到达的范围最远的,即 \(r\) 最大的。这样的话,可以保证我们的决策是最优的。
虽然这样,我们的时间复杂度仍然不足以通过本题,我们可以使用一样东西来优化我们的过程:倍增。
我们设 \(f_{i,j}\) 表示从第 \(j\) 个位置开始跳到往后第 \(2^i\) 条线段的最远点,显然可以预处理出来 \(f\) 数组,这样我们就可以每次 \(\log n\) 求出每次操作的解。
实现
信息开一个结构体存储左右边界和线段的编号。
我们可以先根据每条线段的 \(l\) 进行升序排序,再用双指针的思想找到合法范围内能跳的更远的线段。
根据 \(n\le 2\times 10^5\),可知 \(f\) 数组只要开到 \(\log_22\times10^5\approx 18\) 即可。
注意边界问题。
貌似还有 \(O(n)\) 做法,请移至FlashHu 大佬的博客。
代码就不放了,太占地。
P6902 [ICPC2014 WF] Surveillance
严重怀疑 SCOI2015 借鉴 ICPC2014 WF。
题意
与上一题类似,唯一不同之处就是不保证环能被完全覆盖。
数据范围:\(3\le n\le 10^6\),\(1\le k\le 10^6\)。
思路
一个小细节:注意本题的每两条相邻线段可以不相交,但是必须相邻。在本题中,样例 3 是有解的,但是如果在上一题,样例 3 是不合法的数据。
剩下的与上题类似。我们还是破环成链,排序,预处理 \(f\) 数组,每个点倍增跳父亲,处理合法结果即可。
时间复杂度应该是 \(O(n\log n)\)。
实现及代码
本题可以考虑使用 pair 存储所有节点,pair 自带比较函数可以不用再手写 cmp 函数,\(f\) 数组开到 \(20\) 就够了。
注意边界问题。
后话:
本文是蒟蒻当天集训完后抽时间写的一篇题解。由于学习时间较短,可能内容有待完善,一些错误仅凭蒟蒻难以避免,还望各位大佬指出,蒟蒻不胜感激。