CF2001D Color Rows and Columns

题目链接

题解

知识点:贪心,STL。

显然,子序列最长长度是数的种类数,即保证每个数都会被选到。子序列的奇数位要尽可能大、偶数位尽可能小。

我们从左到右依次选择子序列的数,为了保证每个数都能被选到,我们预处理出每个数的最晚出现位置 lst 。每次选择,只有在当前还未选择的数的 lst 的最小值之前(包括最小值位置),上一个选择位置之后(不包括上一个选择位置),并且还未被选择的数,是可以被选择的。这些可选数中,我们需要选择最大(最小)、位置靠前(给后面的数最多的被选机会)的数。

因此,我们要维护 lst 值的大小顺序,并且支持删除,需要用一个 set<int> 维护。

同时,再用两个 set<pair<int,int>> 维护待选数的大小顺序、位置顺序。

每次选择之前,将位置在最小位置之前的数加入待选数集合,将在上一个选择位置之前的数都删除。注意,每次选择时,遍历待选数集合删除效率很低,因此我们只对选择时遇到的数判断是否删除,如此每个数只会判断一次。

在所有数都被选择过后,生成的序列即为答案。

时间复杂度 O(nlogn)

空间复杂度 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;
}
posted @   空白菌  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示