题意

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