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

时间: 2024-10-05 05:50:12

codeforces 1285E. Delete a Segment的相关文章

Codeforces 242E. XOR on Segment (二维线段树 lazy操作 xor)

题目链接: http://codeforces.com/problemset/problem/242/E 题意: 给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算. 思路: from: http://blog.csdn.net/u013912596/article/details/39006317 很显然直接暴力是不可能的,又是两种操作,又想到了线段树..但是这并不简单,异或操作该怎么处理? 异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j

E. Delete a Segment(删除一个区间,让并区间最多)

题:https://codeforces.com/contest/1285/problem/E 题意:给定n个区间,最多删除一个区间,让最后的并区间个数最大 #include<bits/stdc++.h> using namespace std; const int M=4e5+5; pair<int,int>a[M]; int countt[M]; int main(){ int t; scanf("%d",&t); while(t--){ int n

codeforces 242E. XOR on Segment 线段树

题目链接 给n个数, 两种操作, 一种是求区间内的数的和, 一种是将区间内的数异或x. 异或x没有什么思路, 单个异或肯定超时, 区间异或也没有办法做....后来才知道可以按位建线段树, 这样建20棵线段树就可以. 每一次异或, 对于给定的x, 如果x的第i位是1, 那么就将第i棵线段树在给定的区间内0,1翻转, 这是很基础的操作. 对于区间求和操作, 我们可以求出给定的区间, 从高位到低位, 每一位依次有多少个1, 然后就可以直接求出来, 感觉不好表达....具体看代码. 1 #include

CodeForces 616D Longest k-Good Segment

用队列维护一下即可 #include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int maxn=500000+10; int n,k; int a[maxn]; int tot[1000000+10]; struct Node { int id; int val; } node[maxn]; queue<Node&g

Codeforces 242E. XOR on Segment

题目大意: 给出一个序列,有两种操作,一种是计算l到r的和,另一种是让l到r的数全部和x做异或运算. 做法: 很显然直接暴力是不可能的(但是这题刚刚出来的时候,很多人用暴力水过去了,后来加强的数据吧),又是两种操作,又想到了线段树..但是这并不简单,异或操作该怎么处理? 异或是一种位运算,如果x的第j位是1,那么说明l到r的每个数的第j位都要反转,(0^1=1,1^1=0),如果是0,那么不变.既然是位运算,那么可不可以将每一位作为线段树单独维护呢? 好像可以呢!异或操作的话,相当于是一种区间操

【cf1285E】E. Delete a Segment(vector+二分)

传送门 题意: 给出\(n\)个区间,最终区间会合并为多个块. 现在要删除一个区间,问最终剩下的块最多是多少个. 思路: 将区间按左端点排序后,考虑维护区间的前后缀,然后枚举要删除的区间: 处理起来较麻烦,且维护的信息很多: 所以直接维护前缀信息,然后倒着来枚举删除区间,同时动态维护后缀: 统计答案时需要在后缀区间中二分,时间复杂度为\(O(nlogn)\). 细节见代码: /* * Author: heyuhhh * Created Time: 2020/1/27 10:21:46 */ #i

HDOJ 5372 Segment Game 树状数组+离散化

因为这题的线段长度是递增的....所以: 题解:对于新插入的线段,查询有多少个线段左端点大于等于该线段的左端点. 再查询有多少个线段的右端点大于该线段右端点, 两者之差就是答案.用两个树状数组搞定.时间复杂度nlog Segment Game Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 975    Accepted Submiss

hdu5372 Segment Game

Problem Description Lillian is a clever girl so that she has lots of fans and often receives gifts from her fans. One day Lillian gets some segments from her fans Lawson with lengths of 1,2,3... and she intends to display them by adding them to a num

当前插入的线段能完整覆盖存在的几条线段 树状数组 HDU 5372 Segment Game

http://acm.hdu.edu.cn/showproblem.php? pid=5372 Segment Game Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1284    Accepted Submission(s): 375 Problem Description Lillian is a clever girl so