https://codeforces.com/contest/1249/problem/D2
这道题就是贪心选择最右的一个区间。重点是对区间操作的一些技巧。考试的时候考虑用线段树。其实完全没有必要。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
vector<pair<int, int>> segs(N);
int cnt[N]; // 对于一个区间【a, b】每个元素要加一。则cnt[a]++, cnt[b + 1]--。求前缀和之后,【a,b】之间的元素就全为1
vector<vector<int>> envs(N); // envs[i]记录了以i这个点开始和结束的区间的号码。大于0为开始点。小于0为结束点
int main()
{
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i++)
{
cin >> segs[i].first >> segs[i].second;
++cnt[segs[i].first];
--cnt[segs[i].second + 1];
envs[segs[i].first].push_back(i);
envs[segs[i].second + 1].push_back(-i); // 这里要注意区间[a,b]。b+1的时候才说明走出了区间
}
for (int i = 0; i + 1 < N; ++i) {
cnt[i + 1] += cnt[i];
}
/*
sub也很有意思。对一段区间[a,b]减1。在a开始的时候令cursub+1。令sub[b+1]=-1。到了b+1的时候,cursub += sub[b+1].
就相当于走出这个区间了。
*/
vector<int> ans(N), sub(N);
set<pair<int, int>> cursegs;
int cursub = 0;
for(int i = 0; i < N; i++)
{
cursub += sub[i];
// 把包含这个点所有区间都加进来
for(auto segindex : envs[i])
{
if(segindex > 0) cursegs.insert({segs[segindex].second, segindex});
else
{
// 要走出i这个位置了,把i这个位置结尾的全删掉
auto it = cursegs.find({segs[-segindex].second, -segindex});
if(it != cursegs.end()) cursegs.erase(it);
}
}
while(cnt[i] - cursub > k)
{
assert(!cursegs.empty());
// 贪心的找出最右的区间
int rightindex = cursegs.rbegin()->second;
cursegs.erase(prev(cursegs.end()));
sub[segs[rightindex].second + 1]--;
cursub++;
ans[rightindex] = 1;
}
}
cout << accumulate(ans.begin(), ans.end(), 0) << endl;
for(int i = 1; i <= n; i++)
if(ans[i]) cout << i << " ";
cout << endl;
return 0;
}