题目描述
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;
}