[CF1278D] Segment Tree - 扫描线,并查集,set
Description
给定 n 条线段,每个线段包括起始坐标,如果两条线段严格相交(非相连),则两条线段间有一条边,问能否构成一颗树。
Solution
很容易想到按左端点排序,因为对于线段 a,b,如果 a.l < b.l,则 a,b 相交当且仅当 b.l < a.r < b.r
判断树的问题,很容易想到并查集
我们将所有线段按左端点排序后扫描一遍,维护一个 set,每次扫描到线段 i 时,先检查满足 r 在 i.l 和 i.r 之间的线段是否存在,如果能找到,就在并查集中合并,然后再将 i 加入 set 中,因此这个 set 是按线段的 r 排序的
成树的充要条件是:在合并过程中,没有出现重复合并的情况,并且整个流程执行完后,所有的元素都在同一个集合中
#include <bits/stdc++.h>
using namespace std;
#define int long long
struct Segment
{
int l, r, id;
friend bool operator<(const Segment &lhs, const Segment &rhs)
{
return lhs.r < rhs.r;
};
};
bool cmp(const Segment &lhs, const Segment &rhs)
{
return lhs.l < rhs.l;
}
signed main()
{
ios::sync_with_stdio(false);
int n;
cin >> n;
vector<Segment> seg(n + 2);
for (int i = 1; i <= n; i++)
{
cin >> seg[i].l >> seg[i].r;
seg[i].id = i;
}
sort(&seg[1], &seg[n + 1], cmp);
vector<int> fa(n + 2);
for (int i = 1; i <= n; i++)
fa[i] = i;
function<int(int)> find = [&](int x) -> int {
return x == fa[x] ? x : fa[x] = find(fa[x]);
};
// 我们将所有线段按左端点排序后扫描一遍,
// 维护一个 set,每次扫描到线段 i 时,先检查满足 r 在 i.l 和 i.r 之间的线段是否存在,
// 如果能找到,就在并查集中合并,然后再将 i 加入 set 中,因此这个 set 是按线段的 r 排序的
set<Segment> s;
for (int i = 1; i <= n; i++)
{
auto [ql, qr, id] = seg[i];
auto fir = s.upper_bound({0, ql, 0});
auto las = s.lower_bound({0, qr, 0});
for (auto it = fir; it != las; it++)
{
int x = id, y = it->id;
x = find(x);
y = find(y);
if (x != y)
fa[x] = y;
else
{
cout << "NO" << endl;
return 0;
}
}
s.insert(seg[i]);
}
for (int i = 1; i < n; i++)
if (find(i) != find(n))
{
cout << "NO" << endl;
return 0;
}
cout << "YES" << endl;
}