D. Longest Max Min Subsequence

题目链接: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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值