题目链接:https://codeforces.com/contest/2001/problem/D
题目:
你得到一个整数序列 a1,a2,…,ana1,a2,…,an 。设 SS 为 aa 所有不存在重复元素的非空子序列的集合。你的目标是找到 SS 中最长的序列。如果有多个,在奇数位置的项乘以 −1−1 后,找到最小化字典顺序的那个。
例如,给定 a=[3,2,3,1]a=[3,2,3,1] , S={[1],[2],[3],[2,1],[2,3],[3,1],[3,2],[2,3,1],[3,2,1]}S={[1],[2],[3],[2,1],[2,3],[3,1],[3,2],[2,3,1],[3,2,1]} 。那么 [2,3,1][2,3,1] 和 [3,2,1][3,2,1] 将是最长的,并且 [3,2,1][3,2,1] 将是答案,因为 [−3,2,−1][−3,2,−1] 在字典顺序上小于 [−2,3,−1][−2,3,−1] 。
序列 cc 是序列 dd 的子序列,如果 cc 可以通过删除 dd 中的几个(可能是零或全部)元素来获得。
序列 cc 在字典顺序上小于序列 dd ,当且仅当以下条件之一成立:
- cc 是 dd 的前缀,但 c≠dc≠d ; -在 cc 和 dd 不同的第一个位置,序列 cc 的元素比 dd 中对应的元素要小。
大意:每次给出长度为n的数组,要求找出没有重复元素的,最长的子序列,如果不止一个最长子序列,那么就选择字典序最小的,比较字典序的时候,如果这个元素的下标是奇数,那么就变成负数比较。
思路:map+贪心+双指针
初始指针l=1,r = 1,当我们遇到一个数a[r],这个数是该种类数最后一次出现,也就意味着我们必须选择他,所以我们通过map找l—r最大最小的数x,移动l直到a[l] == x,移动过程中维护l-r区间每个数出现次数,然后删除x,重复以上操作直到x=a[r]结束该操作,移动指针r=r+1。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define edl '\n'
const int N = 1e6 + 10;
const int M = 1e3 + 10;
const int mod = 1e9 + 7;
ll n,m;
string s;
ll a[N];
ll c[N];
map<ll,ll>mp,q;
map<ll,ll>ck;
void solve()
{
ck.clear();
mp.clear();
q.clear();
cin >> n;
for(int i = 1; i <= n; i ++ )
{
cin >> a[i];
mp[a[i]] = i;
}
ll l = 1;
int idx = 0;
for(int i = 1; i <= n; i ++ )
{
if(ck[a[i]]) continue;
q[a[i]] ++;
if(i == mp[a[i]])
{
while(1)
{
++idx;
if(idx % 2) c[idx] = pair(*q.rbegin()).first;
else c[idx] = pair(*q.begin()).first;
ck[c[idx]] = 1;
while(c[idx] != a[l])
{
if(q.count(a[l]))
{
q[a[l]] --;
if(q[a[l]] == 0) q.erase(a[l]);
}
l ++;
}
l ++;
if(q.count(c[idx])) q.erase(c[idx]);
if(l == i + 1 || q.size() == 0 || a[i] == c[idx]) break;
}
}
}
cout << idx << edl;
for(int i = 1; i <= idx; i ++ ) cout << c[i] << " ";
cout << edl;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t = 1;
cin >> t;
while(t -- ) solve();
return 0;
}