#595 (Div. 3) D2.Too Many Segments (hard version)(数据结构:set的应用)

题目描述

The only difference between easy and hard versions is constraints.
You are given n segments on the coordinate axis OX. Segments can intersect, lie inside each other and even coincide. The i-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 k 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 n and k (1≤k≤n≤2⋅105) — the number of segments and the maximum number of segments by which each integer point can be covered.
The next n lines contain segments. The i-th line contains two integers li and ri (1≤li≤ri≤2⋅105) — the endpoints of the i-th segment.

Output

In the first line print one integer m (0≤m≤n) — the minimum number of segments you need to remove so that there are no bad points.
In the second line print m 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

题目大意

在x轴上给出n条线段,每条线段都给出了左右端点l[i]和r[i]。如果有某个点上覆盖了多于k条线段,那么这个点就是一个坏点。问:我们最少要删除多少条线段,使得x轴上不存在坏点。

题目分析

暴力做法我就不写解析了,应该不难看懂。

下面的一些操作正常实现起来是较为复杂的,但应用了数据结构之后,就简单了很多。
首先按照区间的左端点 l 进行排序,然后枚举区间所能覆盖到的每一个点。当这个点大于等于一个区间的左端点时就将这个区间放入set容器中(具体是将区间的 r 和 id 放入set中,因为放入的条件就是l<=i,保证了点i是大于l的;存id是为了方便找到某个区间的下标)。
然后再判断某个区间的右端点是否小于i,如果是则移除该区间。这样就保证了在set容器中的区间都是能覆盖到点i的。
最后再判断这些区间的数量是否大于k,是则要删除一些区间,优先删除r大的(贪心),并记录下删除了的区间。
最后的答案即为这些删除了的区间。

代码如下

暴力版

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
using namespace std;
const int N=210;
struct Node{
	int l,r,id;
}a[N];
bool cmp(Node x,Node y)
{
	if(x.r==y.r) return x.l<y.l;
	return x.r>y.r;
}
int cnt[N];
bool st[N];
int main()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].l>>a[i].r;
		a[i].id=i;
		cnt[a[i].l]++,cnt[a[i].r+1]--;	//差分
	}
	for(int i=1;i<N;i++)	//前缀和
		cnt[i]+=cnt[i-1];
	
	sort(a+1,a+1+n,cmp);
	int ans=0;
	for(int i=1;i<N;i++)
	{
		while(cnt[i]>k)
		{
			int p;
			for(int j=1;j<=n;j++)
				if(a[j].l<=i&&a[j].r>=i&&!st[j])
				{
					ans++;
					st[j]=true;
					p=j;
					break;
				}
			for(int j=a[p].l;j<=a[p].r;j++)
				cnt[j]--;
		}
	}
	cout<<ans<<endl;
	for(int i=1;i<=n;i++)
	if(st[i]) cout<<a[i].id<<' ';
	return 0;
}

优化版 O(logn)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=2e5+5;
struct Node{
	int l,r,id;
}a[N];
bool cmp(Node x,Node y)		//按l从小到大排序
{
	return x.l<y.l;
}
set<PII> se;
vector<int> ans;	//记录删除区间的下标
int main()
{
	int n,k;
	scanf("%d %d",&n,&k);
	int max=0,min=1e9;
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d",&a[i].l,&a[i].r);
		max=std::max(max,a[i].r);	//找出涉及的范围
		min=std::min(min,a[i].l);
		a[i].id=i;
	}
	sort(a+1,a+1+n,cmp);
	int pos=1;
	for(int i=min;i<=max;i++)
	{
		while(pos<=n&&a[pos].l<=i)
		{
			se.insert(make_pair(a[pos].r,a[pos].id));
			pos++;
		}
		while(se.size()&&se.begin()->first<i)
		{
			se.erase(se.begin());
		}
		while(se.size()>k)
		{
			PII t=*(--se.end());
			ans.push_back(t.second);
			se.erase(t);
		}
	}
	printf("%d\n",ans.size());
	for(int it:ans)
	{
		printf("%d ",it);
	}
	puts("");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lwz_159

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值