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 行)

浙公网安备 33010602011771号