[CF1278D] Segment Tree

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

原文地址:https://www.cnblogs.com/mollnn/p/14352520.html