CF1364C Ehab and Prefix MEXs

文章讲述了如何使用贪心策略解决一个编程问题,给定一个长度为n的序列A,需构造一个B序列,使得B的第i项是A前i项中缺失的最小非负整数。当A中连续元素相同时,采用特殊贪心方法确定占位数,确保B的值在0到1e6范围内。
摘要由CSDN通过智能技术生成

tags

构造 贪心 思维

中文题面

题目描述

给出一个长度为 n 的序列 A,你需要找到一个长度为 n 的序列 B,满足 Ai=mex({B1,B2,…,Bi})。

其中 mex 函数的结果是最小的未出现在集合中的非负整数。
输入格式

第一行一个整数 n。

之后一行 n 个整数,表示给出的序列 A。

保证 1≤n≤10^5,0≤ai≤i,对于 1≤i<n,ai≤ai+1​。
输出格式

如果序列 B 不存在,输出 -1。

否则输出一行 n 个整数表示你找到的 B,若有多个满足条件的序列,输出任意一个。
输入输出样例
输入 #1

3
1 2 3

输出 #1

0 1 2

输入 #2

4
0 0 0 2

输出 #2

1 3 4 0

输入 #3

3
1 1 3

输出 #3

0 2 1

思路

经过手写举例和对题目样例的分析后发现,当a[i] != a[i - 1]即数字发生变化时构造b[i] = a[i - 1]是最好的选择,因为当a[i]仍在场时我们保证a[i]为未加入数集中的最小非负整数(mex),而当a[i]离场时我们必须改变这个mex为a[i + 1],而a[i + 1] > a[i],则把a[i]加入已有数集是必要的
现在只剩下a[i] == a[i - 1]这一种情况需要考虑,我们不妨用一种贪心策略,试想在数字变化(假设变换为c)时,我们只能操作一步(构造一次b[i])使得mex变化,则在变化前应当尽可能使a[i]成为c值之下唯一未被加入数集的(如果c值之下除a[i]的所有数都已被加入数集,但仍为变化数字,此时应继续向数集中依次加入除未遍历过但在a数组中的值之外的值)

特别地,题目要求0 <= b[i] <= 1e6,这就意味着我们不能无限贪心,它有一个跳出的时间点
当贪心的数字超过1e6时说明a[i] == a[i - 1]情况下已不需要往数集中加入新数字了,这时候我们只需要在再次遇到a[i] == a[i - 1]时使用某数来占位即可了。
那如何确定这个数呢?
我们考虑找一个不在a数组中的数,这样就不会受到限制,只需遍历最多一整遍0到1e6,找到其中未在a中出现过的数即为我们需要的占位数
突然发现a数组最大为1e5而b数组最大为1e6,则1e5到1e6中间任何一个数都必然不会在a中出现,可以作为占位数

代码

// Edit by Mr_Way
#include <bits/stdc++.h>
using namespace std;


// Edit by Mr_Way
int f = 1;
int a[1000005];
int ban[1000005];
int main() {
    int n, tmp = 1e5+1;
    int now = 0;
    cin >> n;
    for (register int i = 1; i <= n; i++) {
        cin >> a[i];
        ban[a[i]] = 1;
        if (a[i] > i) f = 0;
    }
    if (!f) {
        cout << -1;
        return 0;
    }
    // for (register int i = 0; i <= 1e6; i++) {
    //     if (!ban[i]) {
    //         tmp = i;
    //         break;
    //     }
    // }
    for (register int i = 1; i <= n; i++) {
        if (i > 1 && a[i] != a[i - 1]) cout << a[i - 1] << ' ';
        else {
            while (true) {
                if (!ban[now]) {
                    if (now > 1e6) cout << tmp << ' ';
                    else cout << now++ << ' ';
                    break;
                } else now++;
            }
        }
    }
    return 0;
}
// Edit by Mr_Way
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值