题面:https://codeforc.es/contest/1187/problem/D
给出a、b两个数组,问对a进行部分区间排序操作(可无限次进行),能否得到b数组。
这题被hack炸了,愣是搞出173个测试样例。题解给出了一个有趣的结论:一个数可以往前移动的个数是前面所有数都比他大的区间有多长。
有了这个结论之后我们就可以对b数组每次都贪心的取a数组最左边的ai = bj的位置,然后检查ai能否被移动到开头(使用线段树来检查),如果可以,就把它移动到开头。
ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 5;
//查询最左边的,与当前元素相等的
//还要判断区间最值
int _, n, a[maxn], b[maxn];
int t[maxn << 2];
queue<int> q[maxn];
void pushUp(int i) {
t[i] = min(t[i << 1], t[i << 1 | 1]);
}
void build(int i, int l, int r) {
if (l == r) {
t[i] = a[l];
return;
}
int mid = (l + r) >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
pushUp(i);
}
void update(int i, int l, int r, int pos, int val) {
if (l == r) {
t[i] = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) {
update(i << 1, l, mid, pos, val);
} else {
update(i << 1 | 1, mid + 1, r, pos, val);
}
pushUp(i);
}
int query(int i, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return t[i];
}
int mid = (l + r) >> 1;
int ans = maxn;
if (L <= mid) {
ans = min(ans, query(i << 1, l, mid, L, R));
}
if (R > mid) {
ans = min(ans, query(i << 1 | 1, mid + 1, r, L, R));
}
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin >> _;
while (_--) {
cin >> n;
for (int i = 1; i <= n; ++i) {
while (!q[i].empty()) {
q[i].pop();
}
}
for (int i = 1; i <= n; ++i) {
cin >> a[i];
q[a[i]].push(i);
}
build(1, 1, n);
for (int i = 1; i <= n; ++i) {
cin >> b[i];
}
bool flag = 1;
for (int i = 1; flag && i <= n; ++i) {
if (q[b[i]].empty()) {
flag = 0;
break;
}
int minn = query(1, 1, n, 1, q[b[i]].front());
if (minn < b[i]) {
flag = 0;
break;
}
update(1, 1, n, q[b[i]].front(), maxn);
q[b[i]].pop();
}
if (flag) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}
return 0;
}