codeforces 1285E. Delete a Segment

时间:2020-01-14
本文章向大家介绍codeforces 1285E. Delete a Segment,主要包括codeforces 1285E. Delete a Segment使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

链接:https://codeforces.com/problemset/problem/1285/E

题意:给一个数轴上有n个线段集,线段集若有相交,则合并为一个新的合并线段集,比如[1,6]和[2,9],因为两个线段有相交,所以要合并为[1,9],先问删掉给定的n个线段集中的任意一个,剩下的n-1个线段组成的新的合并线段集数量最大是多少?

思路:

这道题首先想到的是并查集做法,枚举删除任意一条线段后,剩下的线段组成的集合是多少,取max,这个复杂度有n2 × 并查集复杂度,显然是不行的。那么考虑离散化处理线段左右端点,然后去扫描。

如图所示,线段1,2,3,4离散化处理左右端点,然后排个序并标记一下线段号,开始扫描,扫描到的端点先放入multiset集合中去,最初扫描到线段1的左端点L1,然后是L2,再然后是R1,此时我们发现,线段1已经扫描完比,那么删除这条线段1的左右端点,发现集合中只剩下了L2,且下一个要扫描到的端点是L3,此时就意味着删除点线段2后,线段1和线段3是不相交的,那么删除线段2后,新线段合并集合数量就会+1。同理当扫描到R2时,整个线段2已经扫描完毕,删除集合中线段2左右端点,集合只剩下线段3的左端点,且下一个元素是线段4的左端点,说明线段2和线段4不相交,那么此时删除线段3,就意味着线段2和线段4不相连,新线段合并集合数量就+1,根据以上这个性质,扫描的时候枚举下个元素是左端点还是右端点,记录删除每个线段删除后,合并集合会增加多少,最终拿最初不删除任何线段得到的合并线段集数量+max(删除某一线段增加的数量)就是答案

当然要特判一种情况:

 如图所示线段1这种情况,删除这条线段,则新合并集是-1,因为它没有和任何线段有相交,本身就构成一个独立的合并线段集,删除就减少1个合并线段集,特判这种情况即可。

AC代码:

 1 #include<iostream>
 2 #include<vector>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<set>
 8 #include<cstring>
 9 #include<queue>
10 #include<map>
11 using namespace std;
12 typedef long long ll;
13 const int maxn = 4e5+10;   
14 pair<ll,ll> p[maxn];
15 int cnt[maxn];
16 int main()
17 {
18     int t;
19     cin>>t;
20     while(t--){
21         int n;
22         cin>>n;
23         for(int i = 1;i<=n;i++){
24             ll l,r;
25             cin>>l>>r;
26             p[2*i-1] = make_pair(l,-i);//离散化记录区间左右端点的位置和区间标号i 
27             p[2*i] = make_pair(r,i); 
28             cnt[i] = 0;
29         }
30         sort(p+1,p+2*n+1);
31         int ans = 0;
32         multiset<int> s;
33         for(int i = 1;i<=2*n;i++){
34             if(p[i].second < 0){//如果是左端点,就插入set 
35                 s.insert(-p[i].second );
36             }
37             else{
38                 s.erase(s.find(p[i].second));//如果是右端点,就把这个区间删除 
39             }
40             if(s.size() == 0) ans++;//如果集合是空,记录一个合并的区间 
41             if(s.size() == 1 && p[i].second > 0 && p[i+1].second < 0 && p[i+1].first > p[i].first ){
42                 cnt[*s.begin()]++;//当前是左端点,但是下个是右端点,cnt++ 
43             }
44             if(s.size() == 1 && p[i].second < 0 && p[i+1].second >0){
45                 cnt[*s.begin()]--;//如果首先插入地是一段单独区间(l r),去掉这个区间则区间数量-1 
46             }
47         }
48         int t = -1;
49         for(int i = 1;i<=n;i++){
50             t = max(t,cnt[i]);
51         }
52         cout<<ans+t<<endl;
53     }
54     return 0;
55 }

原文地址:https://www.cnblogs.com/AaronChang/p/12192295.html