题解
知识点:贪心,STL。
显然,子序列最长长度是数的种类数,即保证每个数都会被选到。子序列的奇数位要尽可能大、偶数位尽可能小。
我们从左到右依次选择子序列的数,为了保证每个数都能被选到,我们预处理出每个数的最晚出现位置 \(lst\) 。每次选择,只有在当前还未选择的数的 \(lst\) 的最小值之前(包括最小值位置),上一个选择位置之后(不包括上一个选择位置),并且还未被选择的数,是可以被选择的。这些可选数中,我们需要选择最大(最小)、位置靠前(给后面的数最多的被选机会)的数。
因此,我们要维护 \(lst\) 值的大小顺序,并且支持删除,需要用一个 set<int>
维护。
同时,再用两个 set<pair<int,int>>
维护待选数的大小顺序、位置顺序。
每次选择之前,将位置在最小位置之前的数加入待选数集合,将在上一个选择位置之前的数都删除。注意,每次选择时,遍历待选数集合删除效率很低,因此我们只对选择时遇到的数判断是否删除,如此每个数只会判断一次。
在所有数都被选择过后,生成的序列即为答案。
时间复杂度 \(O(n \log n)\)
空间复杂度 \(O(n)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int a[300007], lst[300007];
bool solve() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i], lst[a[i]] = i;
set<int> st;
for (int i = 1;i <= n;i++) if (lst[i]) st.insert(lst[i]);
int pos = 1, pre = 0;
set<pair<int, int>> st_min, st_max;
vector<int> ans;
while (st.size()) {
while (pos <= *st.begin()) {
if (lst[a[pos]]) {
st_min.insert({ a[pos], pos });
st_max.insert({ -a[pos], pos });
}
pos++;
}
while (!lst[st_min.begin()->first] || st_min.begin()->second <= pre) st_min.erase(st_min.begin());
while (!lst[-st_max.begin()->first] || st_max.begin()->second <= pre) st_max.erase(st_max.begin());
int val = ans.size() & 1 ? st_min.begin()->first : -st_max.begin()->first;
pre = ans.size() & 1 ? st_min.begin()->second : st_max.begin()->second;
ans.push_back(val);
st.erase(lst[val]);
lst[val] = 0;
}
cout << ans.size() << '\n';
for (auto val : ans) cout << val << ' ';
cout << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}
标签:begin,Rows,Color,位置,second,st,选择,lst,Columns
From: https://www.cnblogs.com/BlankYang/p/18427231