L. Permutation Compression
数据结构。
首先要让 \(a\) 删除数后得到 \(b\),显然要满足 \(b\) 是 \(a\) 的子集。
因为每次删除的都是最大值,考虑从大往小枚举可删除的数,找左右两边比当前数大的位置,假设 \(l\),\(r\) 代表左右两边比当前数大的位置,那么 \([l+1,r-1]\) 这个区间内都以 \(i\) 为最大值,那么只要存在 \(L_i\) 小于等于 \(r-l-1\) 就可以把 \(i\) 删掉;把 \(i\) 删掉后,为了防止 \(i\) 对后续的影响,需要标记一下,在后续有 \(j\) 找两边最大值时越过了 \(i\) 的位置,需要减掉它的影响,所以对 \(j\) 来说,删除 \(j\) 的区间长度为 \(r-l-1-cnt\),\(cnt\) 代表 \([l+1,r-1]\) 区间内有多少个比 \(j\) 大的数被删掉了,可以用线段树维护这个过程。
最后比较一下每个数可删除的区间长度是不是都能找到一个 \(L_i\) 对应即可,可以将两者排序后一一比较即可。
点击查看代码
#include <bits/stdc++.h>using i64 = long long;template<class Info>
struct SegmentTree {int n;std::vector<Info> info;SegmentTree() : n(0) {}SegmentTree(int n_, Info v_ = Info()) {init(n_, v_);}template<class T>SegmentTree(std::vector<T> init_) {init(init_);}void init(int n_, Info v_ = Info()) {init(std::vector(n_, v_));}template<class T>void init(std::vector<T> init_) {n = init_.size();info.assign(4 << std::__lg(n), Info());std::function<void(int, int, int)> build = [&](int p, int l, int r) {if (r - l == 1) {info[p] = init_[l];return;}int m = (l + r) / 2;build(2 * p, l, m);build(2 * p + 1, m, r);pull(p);};build(1, 0, n);}void pull(int p) {info[p] = info[2 * p] + info[2 * p + 1];}void modify(int p, int l, int r, int x, const Info &v) {if (r - l == 1) {info[p] = v;return;}int m = (l + r) / 2;if (x < m) {modify(2 * p, l, m, x, v);} else {modify(2 * p + 1, m, r, x, v);}pull(p);}void modify(int p, const Info &v) {modify(1, 0, n, p, v);}Info rangeQuery(int p, int l, int r, int x, int y) {if (l >= y || r <= x) {return Info();}if (l >= x && r <= y) {return info[p];}int m = (l + r) / 2;return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);}Info rangeQuery(int l, int r) {return rangeQuery(1, 0, n, l, r);}template<class F>int findFirst(int p, int l, int r, int x, int y, F &&pred) {if (l >= y || r <= x) {return -1;}if (l >= x && r <= y && !pred(info[p])) {return -1;}if (r - l == 1) {return l;}int m = (l + r) / 2;int res = findFirst(2 * p, l, m, x, y, pred);if (res == -1) {res = findFirst(2 * p + 1, m, r, x, y, pred);}return res;}template<class F>int findFirst(int l, int r, F &&pred) {return findFirst(1, 0, n, l, r, pred);}template<class F>int findLast(int p, int l, int r, int x, int y, F &&pred) {if (l >= y || r <= x) {return -1;}if (l >= x && r <= y && !pred(info[p])) {return -1;}if (r - l == 1) {return l;}int m = (l + r) / 2;int res = findLast(2 * p + 1, m, r, x, y, pred);if (res == -1) {res = findLast(2 * p, l, m, x, y, pred);}return res;}template<class F>int findLast(int l, int r, F &&pred) {return findLast(1, 0, n, l, r, pred);}
};constexpr int inf = 1E9;struct Info {int max = -inf, cnt = 0;
};Info operator+(const Info &l, const Info &r) {return {std::max(l.max, r.max), l.cnt + r.cnt};
}void solve() {int n, m, k;std::cin >> n >> m >> k;SegmentTree<Info> seg(n);std::vector<int> a(n), b(m), c(k), vis(n), pos(n);for(int i = 0; i < n; i += 1) {std::cin >> a[i];a[i] -= 1;pos[a[i]] = i;seg.modify(i, {a[i], 0});}for(int i = 0; i < m; i += 1) {std::cin >> b[i];b[i] -= 1;vis[b[i]] = 1;}for(int i = 0; i < k; i += 1) {std::cin >> c[i];}int idx = 0;for(int i = 0; i < n; i += 1) {if(idx < m && a[i] == b[idx]) {idx += 1;}}if(idx != m) {std::cout << "NO\n";return;}std::vector<int> sz;for(int i = n - 1; i >= 0; i -= 1) {if(vis[i]) continue;int l = -1,r = n;auto x = seg.findLast(0, pos[i], [&](auto p) {return p.max > i;});l = x;x = seg.findFirst(pos[i] + 1, n, [&](auto p) {return p.max > i;});if(x != -1) {r = x;}l += 1, r -= 1;sz.push_back(r - l + 1 - seg.rangeQuery(l, r + 1).cnt);seg.modify(pos[i], {0, 1});}if(c.size() < sz.size()) {std::cout << "NO\n";return;}sort(c.begin(), c.end());sort(sz.begin(), sz.end());for(int i = 0; i < sz.size(); i += 1) {if(c[i] > sz[i]) {std::cout << "NO\n";return;}}std::cout << "YES\n";}
int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int t;std::cin >> t;while (t--) {solve();}return 0;
}