You are given nn colored segments on the number line. Each segment is either colored red or blue. The ii-th segment can be represented by a tuple (ci,li,ri)(ci,li,ri). The segment contains all the points in the range [li,ri][li,ri], inclusive, and its color denoted by cici:
- if ci=0ci=0, it is a red segment;
- if ci=1ci=1, it is a blue segment.
We say that two segments of different colors are connected, if they share at least one common point. Two segments belong to the same group, if they are either connected directly, or through a sequence of directly connected segments. Find the number of groups of segments.
Input
Each test contains multiple test cases. The first line contains the number of test cases tt (1≤t≤1051≤t≤105). Description of the test cases follows.
The first line of each test case contains a single integer nn (1≤n≤1051≤n≤105) — the number of segments.
Each of the next nn lines contains three integers ci,li,rici,li,ri (0≤ci≤1,0≤li≤ri≤1090≤ci≤1,0≤li≤ri≤109), describing the ii-th segment.
It is guaranteed that the sum of nn over all test cases does not exceed 105105.
Output
For each test case, print a single integer kk, the number of groups of segments.
Example
input
2 5 0 0 5 1 2 12 0 4 7 1 9 16 0 13 19 3 1 0 1 1 1 2 0 3 4
output
2 3
题意: 给出若干个红色或蓝色的线段,每条线段都有一个左端点和右端点,若两线段存在交集且颜色不用,则这两线段属于同一集合,问最终有多少个不同集合。
分析: 先不考虑颜色,如果有若干线段问最终不同集合数,这个问题可以对所有线段的左端点和右端点混合起来进行排序,这样就得到了2n个从小到大排序的点,然后扫描这些点,当遇到左端点那就代表把对应的线段加入集合,这时候可以把该线段和集合中已有的线段进行连边,如果遇到右端点那就删除对应的左端点,代表把这条线段从集合中移除。不过加入左端点时如果暴力连边复杂度最后就变成了O(n^2)了,其实可以用集合中右端点最大的那条线段来代替,这样复杂度就变成了O(n),如果不用右端点最大的线段代替,而是选择其他线段来代替会出现问题。
现在考虑两种不同的颜色,同样是将所有线段2n个点进行排序,然后扫描这些点,遇到右端点就将对应线段删除,遇到左端点就将对应线段加入集合,同时看一下集合中不同颜色的线段,让当前这条线段和不同颜色的所有线段进行连边,不过只保留右端点最长的那条线段,其余线段全部移除,因为多余的线段都被移除了,所以复杂度还是O(n)的,不会进行太多次连边。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#define pii pair<int, int>
using namespace std;
int fa[100005];
struct seg{
int col, l, r;
}a[100005];
struct node{
int val, id, l;
};
bool cmp(node x, node y){
if(x.val != y.val) return x.val < y.val;
return x.l > y.l;
}
int find(int x){
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
signed main(){
int T;
cin >> T;
while(T--){
int n;
scanf("%d", &n);
vector<node> v;
for(int i = 1; i <= n; i++){
scanf("%d%d%d", &a[i].col, &a[i].l, &a[i].r);
v.push_back({a[i].l, i, 1});
v.push_back({a[i].r, i, 0});
fa[i] = i;
}
sort(v.begin(), v.end(), cmp);
set<pii> R[2];
for(int i = 0; i < v.size(); i++){
int id = v[i].id, col = a[id].col, l = v[i].l;
if(l){//左端点
R[col].insert(make_pair(a[id].r, id));
if(R[1^col].size() > 1){
while(R[1^col].size() != 1){
pii now = *R[1^col].begin();
R[1^col].erase(R[1^col].begin());
fa[find(id)] = find(now.second);
}
}
if(R[1^col].size() == 1){
pii now = *R[1^col].begin();
fa[find(id)] = find(now.second);
}
}
else{//右端点
R[col].erase(make_pair(a[id].r, id));
}
}
int ans = 0;
for(int i = 1; i <= n; i++)
if(find(i) == i) ans++;
printf("%d\n", ans);
}
return 0;
}