【CodeForces】【贪心】1628A Meximum Array
CodeForces 1628A Meximum Array 题解
·
CodeForces 1628A Meximum Array
题目大意
给定一个由 N N N 个数组成的序列 A A A,现要求执行如下操作直至序列 A A A 为空:
- 从 A A A 中顺次取出 k k k 个元素;
- 求出这 k k k 个元素的 MEX 值;
- 将该值按次序组成一个新的序列 B B B 。
现要求求出字典序最大的 B B B 序列。
MEX 值指一个非负整数集合中未出现的最小非负整数。
分析
这个题我第一眼感觉是一个模拟就可以比较完美解决的题目,但数据范围决定了这个题不能用 O ( N log 2 N ) O(N\log_2 N) O(Nlog2N) 以上的方法去做。而由 MEX 值的定义与字典序最大的要求,我们就必须组一个前面数字较大、总长度要长的 B B B 序列出来。而注意到要求 B B B 序列长度要长的要求,我们在截取时应该截取尽可能少的元素。下面说明这个问题的贪心性质:
记截取到某一个前缀的 MEX 值为 x x x。那么如果我们再往后截取一些比 x x x 大的数,MEX 值不会变而截取的总数变少了,显而易见对答案是不利的;如果往后一个有 x x x,那么 MEX 值就会变大,即对答案有利。所以我们就可以采取贪心算法,每次截取一段时我们先枚举 MEX 值,如果后面有一样的就继续加长一段,否则就截断后截取新一段重复操作。
这样直接做就是 O ( N 2 ) O(N^2) O(N2) 的,但注意到 A A A 序列中所有值都比 N N N 小,所以我们就可以把 A A A 中每个数按值把它的位置扔进一个桶里,然后用二分找后面有没有数。总时间复杂度为 O ( N log 2 N ) O(N \log_2 N) O(Nlog2N)。
参考代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int MaxN = (int)2e5;
int N, A[MaxN + 5];
vector<int> pos[MaxN + 5];
int lowerbound(int num, int p) {
int lb = 0, rb = (int)pos[num].size() - 1;
while(lb <= rb) {
int mid = (lb + rb) >> 1;
if(pos[num][mid] <= p) lb = mid + 1;
else rb = mid - 1;
}
return (lb < pos[num].size() ? pos[num][lb] : -1);
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
vector<int> ans;
scanf("%d", &N);
for(int i = 0; i <= N; i++) pos[i].clear();
for(int i = 1; i <= N; i++)
scanf("%d", &A[i]), pos[A[i]].push_back(i);
int lb = 0, rb = lb + 1;
while(lb < N) {
rb = lb + 1;
for(int i = 0; i <= N; i++) {
int t = lowerbound(i, lb);
if(t == -1) {
ans.push_back(i);
break;
}
rb = max(rb, t);
}
lb = rb;
}
printf("%u\n", ans.size());
for(auto i : ans) printf("%d ", i);
puts("");
}
return 0;
}
更多推荐
所有评论(0)