题目相关
题目链接
HHKB Programming Contest 2020 C 题,https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_c。
Problem Statement
Given is a number sequence of length N: p1,...,pN.
For each i=1,2,...,N, find the minimum non-negative integer that is not equal to any of the numbers p1,...,pi.
Input
Input is given from Standard Input in the following format:
N
P1 ... PN
Output
Print N lines in total.
The i-th line (1≤i≤N) should contain the minimum non-negative integer that is not equal to any of the numbers p1,...,pi.
Samples1
Sample Input 1
4
1 1 0 2
Sample Output 1
0
0
2
3
Explaination
- The minimum non-negative integer that is not equal to p1=1 is 0.
- The minimum non-negative integer that is not equal to any of p1=1,p2=1 is 0.
- The minimum non-negative integer that is not equal to any of p1=1,p2=1,p3=0 is 2.
- The minimum non-negative integer that is not equal to any of p1=1,p2=1,p3=0,p4=2 is 3.
Samples2
Sample Input 2
10
5 4 3 2 1 0 7 7 6 6
Sample Output 2
0
0
0
0
0
6
6
6
8
8
Constraints
- 1≤N≤200,000
- 0≤pi≤200,000
- All values in input are integers.
题解报告
肝完一个难题,写个水题压压惊。
题目翻译
给一个长度为 N 的序列,记为 P1, P2, ..., PN。要求我们找序列第 i 个数列中,找出最小的非负数,而且这个数没有在 P1, P2, ..., Pi 中。
题目分析
AtCoder 的 C 题是一个玄学,有时候会很水,比如这次,考数据结构的知识点。
要找出最小的非零负数,而且这个数必须没有在 P1, P2, ..., Pi 列中出现。那就很简单了,自己记录一下出现的数据即可,也就是在出现的数据做标记,然后每个询问,查找第一个没有标记的就是答案了。
但是要注意,本题有 N 个查询,如果每次都是从 0 到 2e5 查询的话,数据量大,会出现 TLE 哦。因为你的复杂度是 O(N^2) 的。
简单优化一下就可以了。也就是记录上次找到的 ans,下一次查询,从上次 ans 位置开始查询即可。
当然,本题最简单的思路是使用 STL 的 set。也就是,每次读入一个数据,从 set 中将这个数据删除,每次的答案就是 set 的开始位置。
样例数据 2 分析
看不明白,我们来分析一下样例数据。样例数据 1 比较小,我们选择样例数据 2。
根据样例 2,我们可以知道 N=10,读入的数据次序为:5 4 3 2 1 0 7 7 6 6。
初始状态
我们可以定义一个 11 大小状态数组,注意一定要比 N 大,可以考虑一下为什么?我们用 0 表示没有出现,用 1 表示出现。那么这个数组的初始状态为:
f[0]=0;
f[1]=0;
f[2]=0;
f[3]=0;
f[4]=0;
f[5]=0;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
用 ans 表示上次回答,因此 ans 初始化也为 0。
第一个数据 5
读取数据为 5。这样我们先将 f[5] 设置为 1。这样状态数组变为:
f[0]=0;
f[1]=0;
f[2]=0;
f[3]=0;
f[4]=0;
f[5]=1;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=0。
第二个数据 4
读取数据为 4。这样我们先将 f[4] 设置为 1。这样状态数组变为:
f[0]=0;
f[1]=0;
f[2]=0;
f[3]=0;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=0。
第三个数据 3
读取数据为 3。这样我们先将 f[3] 设置为 1。这样状态数组变为:
f[0]=0;
f[1]=0;
f[2]=0;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=0。
第四个数据 2
读取数据为 2。这样我们先将 f[2] 设置为 1。这样状态数组变为:
f[0]=0;
f[1]=0;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=0。
第五个数据 1
读取数据为 1。这样我们先将 f[1] 设置为 1。这样状态数组变为:
f[0]=0;
f[1]=1;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=0。
第六个数据 0
读取数据为 0。这样我们先将 f[0] 设置为 1。这样状态数组变为:
f[0]=1;
f[1]=1;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=0;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=6。
第七个数据 7
读取数据为 7。这样我们先将 f[7] 设置为 1。这样状态数组变为:
f[0]=1;
f[1]=1;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=1;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=6。
第八个数据 7
读取数据为 7。这样我们先将 f[7] 设置为 1。这样状态数组变为:
f[0]=1;
f[1]=1;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=0;
f[7]=1;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=6。
第九个数据 7
读取数据为 6。这样我们先将 f[6] 设置为 1。这样状态数组变为:
f[0]=1;
f[1]=1;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=1;
f[7]=1;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=8。
第十个数据 6
读取数据为 6。这样我们先将 f[6] 设置为 1。这样状态数组变为:
f[0]=1;
f[1]=1;
f[2]=1;
f[3]=1;
f[4]=1;
f[5]=1;
f[6]=1;
f[7]=1;
f[8]=0;
f[9]=0;
f[10]=0;
f[11]=0;
我们从 f[ans] 开始查找第一个为零的数据,并更新 ans 的值。
这样,我们得到 ans=8。
这样我们就得到了最终输出结果。
AC 参考代码
注意一个细节,由于最大数据有 2e5,使用流式输入输出,一定要用快读,否则最后三个测试用例就是 TLE。
不要问为什么,因为我无聊,我测试过。
使用标记法
//https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_c
//HHKB Programming Contest 2020
//C - Neq Min
/*
使用标记法
*/
#include <iostream>
#include <set>
using namespace std;
const int MAXN=2e5+4;
bool f[MAXN];
int main() {
//快读
ios::sync_with_stdio(false);
//cin.tie(0);
int n;
cin>>n;
int x;
int ans=0;
for (int i=0; i<n; i++) {
cin>>x;
//设置标志
f[x]=true;
//查找
while (true==f[ans]) {
ans++;
}
cout<<ans<<"\n";
}
return 0;
}
使用 STL 的 set
//https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_c
//HHKB Programming Contest 2020
//C - Neq Min
/*
使用 STL set
先将 0 ~ 2e5 所有数据插入 set 中。
然后读取数据,有的数据,从 set 中删除。
*/
#include <iostream>
#include <set>
using namespace std;
const int MAXN=2e5+4;
int main() {
//快读
ios::sync_with_stdio(false);
//cin.tie(0);
//讲所有数据标记
set<int> st;
for (int i=0; i<=MAXN; i++) {
st.insert(i);
}
int n;
cin>>n;
int x;
for (int i=0; i<n; i++) {
cin>>x;
st.erase(x);
cout<<*st.begin()<<"\n";
}
return 0;
}
我去,使用标志法的时间还快那么一丢丢。使用 STL 就是代码量少。