[并查集]Number of Groups Codeforces1691E

文章介绍了一个关于线段分组的问题,其中线段有红色和蓝色两种颜色。线段若存在交集且颜色不同,则认为它们属于同一集合。通过排序线段的端点,扫描过程中利用并查集维护集合,确保复杂度在O(n)。最终输出不同颜色线段的集合数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值