Too Many Segments (hard version) CodeForces - 1249D2(贪心+容器vector+set)
題目
給多組線段,而每一個點的覆蓋次數不超過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?105^{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?105^{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<cstdio> #include<cstring> #include<vector> #include<set> #include<algorithm> 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.r;/*右端點從小到大排序*/elsereturn id<v.id;/*下標從小到大排序*/} }; vector<node>v[N];/*vector容器里面存放的是結構體,開了一個v[N]的數組,相當于二維數組*/ vector<int>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<node>s;for(int i=1; i<N; i++){while(s.size()&&(*s.begin()).r<i)/*剛開始是不執行while循環的,若set容器中存的線段的右端點小于下一個點,那么這條邊就可以刪去*/s.erase(*s.begin());for(int j=0; j<v[i].size(); j++) /*遍歷以頂點i為左端點的所有的線段*/s.insert(v[i][j]);/*set容器中存的是這條線段的所有信息,set容器插入元素時,會自動調整二叉樹的結構,此時的‘<’的重載操作就起作用*/while(s.size()>k){dp.push_back((*s.rbegin()).id);s.erase(*s.rbegin());}}printf("%d\n",dp.size());for(int i=0; i<dp.size(); i++)printf("%d ",dp[i]);return 0; }總結
以上是生活随笔為你收集整理的Too Many Segments (hard version) CodeForces - 1249D2(贪心+容器vector+set)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 舒脑欣滴丸功效与作用
- 下一篇: 孕妇能不能吃阿胶糕