CF2001D Color Rows and Columns

题目链接

题解

知识点:贪心,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;
}
posted @ 2024-09-23 16:25  空白菌  阅读(5)  评论(0编辑  收藏  举报