CF1278D Segment Tree(set乱搞

博客探讨了如何解决线段相交形成树的问题。通过对线段按左端点排序,使用set存储和检查右端点,利用并查集维护连通性,判断能否生成一棵树。复杂度为O(nlogn)。

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

题意

n n n 个线段,如果两条线段严格相交,则两条线段之间有一条边。
问最终能否生成一棵树。

分析

设两条线段为 a , b a,b a,b(设 a . l < b . l a.l < b.l a.l<b.l
a , b a,b a,b 相交的条件是:
b . l < a . r < b . r b.l < a.r < b.r b.l<a.r<b.r
于是我们先将线段按左端点排序
然后对于每条线段,它的左端点都是大于之前线段的左端点的,我们只需要看它的右端点大于之前哪些线段的右端点。
于是我们用一个 s e t set set,把之前线段的右端点存起来:
如果这些右端点比当前线段左端点小,就一一删除(显然也无法和后面连边
否则如果这些右端点比当前线段右端点小,就暴力连边,用并查集维护连通性
最后检查有多少个集合即可。

复杂度是咋样的?
连边最多连 n − 1 n-1 n1 次,每条线段最多进出 s e t set set 一次,复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)

代码如下

#include <bits/stdc++.h>
#define N 500005
using namespace std;
struct node{
	int l, r;
	bool operator < (const node & A) const{
		return l < A.l;
	}
}d[N];
set<node> s;
int f[N], cnt;
int find(int x){
	return x == f[x]? x: f[x] = find(f[x]);
}
int main(){
	int i, j, n, m, a, b;
	set<node>::iterator it;
	node t;
	scanf("%d", &n);
	for(i = 1; i <= n; i++){
		scanf("%d%d", &d[i].l, &d[i].r);
		f[i] = i;
	}
	sort(d + 1, d + i);
	t.l = d[1].r, t.r = 1;
	s.insert(t);
	for(i = 2; i <= n; i++){
		while(1){
			if(!s.size()) break;
			t = *s.begin();
			if(t.l <= d[i].l) s.erase(s.begin());
			else break;
		}
		for(it = s.begin(); it != s.end(); it++){
			t = *it;
			if(t.l >= d[i].r) break;
			a = i, b = t.r;
			if(find(a) != find(b)) f[f[b]] = f[a];
			else{
				printf("NO");
				return 0;
			}
		}
		t.l = d[i].r, t.r = i;
		s.insert(t);
	}
	for(i = 1; i <= n; i++) if(i == find(i)) cnt++;
	if(cnt == 1) printf("YES");
	else printf("NO");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值