P8896 题解

首先,观察到题目要求变出一个 DAG,所以我们可以给节点一个拓扑序。设其为 \(a_0, a_1, \cdots, a_{n - 1}\)

因为原图是完全图,所以在 DAG 中,对于所有 \(0 \le i < n\)\(a_i\) 要连 \(a_{i + 1}, a_{i + 2}, \cdots, a_{n - 1}\)。所以其出度为 \(n - i\)

故原问题变成了:给定 \(n\) 个区间 \([l_i, r_i]\),请问是否有长度为 \(n\) 的排列 \(p\) 满足 \(\forall 0 \le i < n, l_i \le p_i \le r_i\)

考虑贪心。我们遍历每个数,然后找出它所在的位置。在搜到 \(i\) 时将左端点为 \(i\) 的所有区间的右端点 push 进优先队列,之后 pop 一个元素出来,然后把 \(i\) 放入这个区间。

如果优先队列是空的,或 pop 的区间不包含 \(i\),则无解。

#include <iostream>
#include <queue>
#include <vector>

using namespace std;
using i64 = long long;

void solve_test()
{
    int n;
    cin >> n;

    vector<vector<int>> segs(n);
    vector<int> lo(n);

    for (int& l : lo)
        cin >> l;

    for (int i = 0; i < n; ++i) {
        int hi;
        cin >> hi;
        segs[lo[i]].emplace_back(hi);
    }

    priority_queue<int> seg;

    for (int lo = 0; lo < n; ++lo) {
        const auto& hi(segs[lo]);

        for (int r : hi)
            seg.emplace(-r);

        if (seg.empty()) {
            cout << "NO\n";
            return;
        }

        int curr = -seg.top();
        seg.pop();

        if (curr < lo) {
            cout << "NO\n";
            return;
        }
    }

    cout << "YES\n";
}

int main()
{
    int t;
    cin >> t;

    while (t-- > 0)
        solve_test();

    return 0;
}

P.S. 向优先队列中 push 负的元素可以让这些元素从小到大排列。(第 31 行)

posted @ 2025-08-02 16:15  David9006  阅读(11)  评论(0)    收藏  举报