Too Many Segments (hard version) CodeForces - 1249D2(贪心+容器vector+set)
发布日期:2021-06-29 04:04:30 浏览次数:2 分类:技术文章

本文共 4581 字,大约阅读时间需要 15 分钟。

题目

给多组线段,而每一个点的覆盖次数不超过K,每次可去除一个线段,问最少去多少线段以及线段的位置。

The only difference between easy and hard versions is constraints.

You are given nn segments on the coordinate axis OX. Segments can intersect, lie inside each other and even coincide. The ii-th segment is [li;ri] (li≤ri) and it covers all integer points j such that li≤j≤ri.

The integer point is called bad if it is covered by strictly more than kk segments.

Your task is to remove the minimum number of segments so that there are no bad points at all.

Input

The first line of the input contains two integers nn and kk (1≤k≤n≤2⋅10 5 ^{5} 5) — the number of segments and the maximum number of segments by which each integer point can be covered.

The next nn lines contain segments. The ii-th line contains two integers lili and riri (1≤li≤ri≤2⋅10 5 ^{5} 5) — the endpoints of the ii-th segment.

Output

In the first line print one integer mm (0≤m≤n) — the minimum number of segments you need to remove so that there are no bad points.

In the second line print mm distinct integers p1,p2,…,pm(1≤pi≤n) — indices of segments you remove in any order. If there are multiple answers, you can print any of them.

Examples

Input
7 2
11 11
9 11
7 8
8 9
7 8
9 11
7 9
Output
3
4 6 7
Input
5 1
29 30
30 30
29 29
28 30
30 30
Output
3
1 4 5
Input
6 1
2 3
3 3
2 3
2 2
2 3
2 3
Output
4
1 3 5 6
官方题解:
In this problem, we need to implement the same greedy solution as in the easy version, but faster. Firstly, let’s calculate for each point the number of segments covering it. We can do it using standard trick with prefix sums: increase cntli, decrease cntri+1 and build prefix sums on the array cnt.

Let’s maintain the set of segments that cover the current point, sorted by the right endpoint. We can do this with almost the same trick: append to the array evli the index i that says us that in the point li the i-th segment is opened. And add to the evri+1 the index −i that says us that in the point ri+1 the i-th segment is closed. Note that you need to add 11-indexed values i because +0 and −0 are the same thing actually. We can change the array cntcnt to carry the number of segments covering each point using some structure, but we don’t need to do it. Let’s maintain the variable curSub that will say us the number of segments covering the current point that was already removed. Also, let’s carry another one array sub which will say us when we need to decrease the variable curSub.

So, we calculated the array of arrays ev, the array cnt and we can solve the problem now. For the point i, let’s remove and add all segments we need, using the array evievi and add subi to curSub. Now we know that the set of segments is valid, curSub is also valid and we can fix the current point if needed. While cnti−curSub>k, let’s repeat the following sequence of operations: take the segment with the maximum right border rmaxrmax from the set, remove it, increase curSub by one and decrease subrmax+1by one.

Note that when we remove segments from the set at the beginning of the sequence of moves for the point ii, we don’t need to remove segments that we removed by fixing some previous points, and we need to pay attention to it.

Time complexity: O(nlogn).

百度翻译:

在这个问题上,我们需要实现与简单版本相同的贪婪解决方案,但速度更快。首先,让我们计算每个点覆盖它的段数我们可以使用带前缀和的标准技巧:增加cntli,减少cntri+1,并在阵列cnt上构建前缀和。

让我们保持覆盖当前点的线段集,按右端点排序我们可以使用几乎相同的技巧来实现这一点:将索引i附加到数组evli中,该索引i表示在点li中,第i段已打开再加上evri+1的指数-i,表示在ri+1点,第i段是闭合的注意,您需要添加11个索引值i,因为+0和-0实际上是相同的我们可以使用某种结构来改变阵列碳纳米管来携带覆盖每个点的段数,但我们不需要这样做。让我们维护一个变量cursub,它将告诉我们覆盖当前点的段数,这个点已经被删除了。另外,让我们携带另一个数组sub,当我们需要减少变量cursub时,它会告诉我们。
因此,我们计算了阵列的ev,阵列的cnt,我们现在可以解决这个问题对于点i,让我们使用数组eviev移除并添加我们需要的所有段,并将subi添加到curSub现在我们知道段集是有效的,curSub也是有效的,如果需要,我们可以修复当前点在重新编译时,让我们重复以下操作序列:从集合中选择最大右边界的段,删除它,将其增加一个,减少一个。
请注意,当我们在第二点的动作序列开始时从集合中移除片段时,我们不需要移除通过修复某些先前点而移除的片段,并且我们需要注意它。
时间复杂度:O(nLogn)。

思路

贪心思想,1. 以区间左端点为头,右端点由小到大排序,如果端点相同,按标号存入。

2.按排好的线段顺序,从头开始遍历每个点,判断每个点上覆盖次数,大于k时删除最长的那个区间。
3.并且真正有用的覆盖点其实就是端点(其余点都可以等价到端点上),,把每个右端点带上标号放进set(记录所有以x为左端点的线段)。

AC代码

#include
#include
#include
#include
#include
using namespace std;const int N=2*1e5+10;struct node{
int id; int r; bool operator <( const node &v)const {
if(r!=v.r) return r
v[N];/*vector容器里面存放的是结构体,开了一个v[N]的数组,相当于二维数组*/vector
dp;int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=1; i<=n; i++) { node q; int x,y; scanf("%d%d",&x,&y); q.id=i; q.r=y; v[x].push_back(q);/*记录所有以x为左端点的线段*/ } set
s; for(int i=1; i
k) { dp.push_back((*s.rbegin()).id); s.erase(*s.rbegin()); } } printf("%d\n",dp.size()); for(int i=0; i

转载地址:https://blog.csdn.net/zeng_jun_yv/article/details/102868111 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Fibonacci Sum HDU - 6755【2020 Multi-University Training Contest 1】斐波那契数列变形+二项式定理
下一篇:Seek the Name, Seek the Fame POJ - 2752 (理解KMP函数的失配)既是S的前缀又是S的后缀的子串

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月27日 01时37分26秒