
Codeforces Round #762 (Div. 3) E题MEX and Increments题解
前言
题目
1.题目大意
- 给你一个由n个非负数构成的数组
- 定义MEX为数组中不存在的最小非负数,例如:
- [3, 1, 0] MEX = 2
- [3, 3, 1, 4] MEX = 0
- 要求使MEX = 0到n需要对数组进行最小的操作次数,无法实现输出-1
- (一次操作就是对数组中任意元素+1)
2.题目分析
- 首先有几个注意要点
- 当前i对应的MEX不满足题意为-1时,后面大于i的数对应的MEX均为-1
- 当前i对应的MEX满足题意时,前面必然可以构造出一个小序列,满足0到i的元素有且只有一个。
- 构造小序列的最新一位i-1时,要从已构造元素的重复元素中选出最接近i-1的一个进行构造。
- 根据要点2以及题目要求,我们可以得到以下公式:
ans = dp + cnt
- 其中:
- ans:要求的最小操作数
- dp:构造小序列所需的最小操作数
- cnt:当前数值元素的个数
- 根据要点3,进一步添加2个优先队列:
- up:升序,用于存储样例中的所有元素
- down:降序,用于存储小于当前i的重复元素
- 以样例中的0 1 2 3 4 3 2为例进行演示
| MEX |
up |
down |
dp |
cnt |
ans |
| 0 |
0 1 2 2 3 3 4 |
∅ |
0 |
1 |
0 + 1 = 1 |
| 1 |
1 2 2 3 3 4 |
0 |
1 - 1 - 0 + 0 = 0 |
1 |
0 + 1 = 1 |
| 2 |
2 2 3 3 4 |
1 |
2 - 1 - 1 + 0 = 0 |
1 |
0 + 1 = 1 |
| 3 |
3 3 4 |
2 2 |
3 - 1 - 2 + 0 = 0 |
2 |
0 + 2 = 2 |
| 4 |
4 |
3 3 2 |
4 - 1 - 3 + 0 = 0 |
2 |
0 + 2 = 2 |
| 5 |
∅ |
4 3 2 |
5 - 1 - 4 + 0 = 0 |
0 |
0 + 0 = 0 |
| 6 |
∅ |
3 2 |
6 - 1 - 3 + 0 = 2 |
0 |
2 + 0 = 2 |
| 7 |
∅ |
2 |
7 - 1 - 2 + 2 = 6 |
0 |
6 + 0 = 6 |
3.题目代码
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int n;
int tmp;
cin >>n;
long long cnt[200005]={0};//有可能有大于n的数,所以数组要开到最大
priority_queue<long long, vector<long long>,greater<long long>> up;//升序
priority_queue<long long, vector<long long>,less<long long>> down;//降序
for(int i=0;i<n;i++)//统计个数
{
cin >> tmp;
cnt[tmp]++;
up.push(tmp);
}
long long dp = 0;//创造小序列的操作数
long long ans = 0;
cout << cnt[0] << " ";
for(int i=1;i<=n;i++)
{
while(!up.empty()&&up.top()<i)
{
down.push(up.top());
up.pop();
if(up.empty())
break;
}
if(down.empty())//构造不出小序列
{
for(int j=i;j<=n;j++)
cout << -1 << " ";
break;
}
else
{
dp += ((i-1) - down.top());//构造小序列
down.pop();
ans = dp + cnt[i];
cout << ans << " ";
}
}
cout << endl;
}
return 0;
}
- ps:在做题的过程中发现对于0的特判可以直接包含进去,于是以上代码就是最后的版本