[10月考试] F
题目描述
给定长度为 nnn 的序列 ana_nan,保证 aia_iai 为非负整数。
mmm 次询问,每次给定区间 l,rl,rl,r,求出 al,al+1,…,ara_l,a_{l+1},\ldots,a_ral,al+1,…,ar 的 mexmexmex。
对于一个序列,定义其 mexmexmex 为不在序列中出现的最小非负整数。
例如序列 1,2,5,71,2,5,71,2,5,7 的 mexmexmex 为 000,序列 3,0,1,2,53,0,1,2,53,0,1,2,5 的 mexmexmex 为 444。
对于所有数据,n,m≤1000n,m\leq 1000n,m≤1000,1≤l≤r≤n1\leq l\leq r\leq n1≤l≤r≤n,0≤ai≤10000\leq a_i\leq 10000≤ai≤1000。
输入格式
输入共 m+2m+2m+2 行。
第 111 行输入 222 个正整数 n,mn,mn,m。
第 222 行输入 nnn 个非负整数 a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,…,an。
接下来 mmm 行,每行输入 222 个正整数 l,rl,rl,r 代表一次询问。
输出格式
输出共 mmm 行,每行输出 111 个非负整数,代表一次询问的答案。
样例 #1
样例输入 #1
5 3
1 3 2 0 4
1 5
2 4
1 3
样例输出 #1
5
1
0
提示
对于 30%30\%30% 的数据,n,m≤5n,m\leq 5n,m≤5。
对于 60%60\%60% 的数据,n,m≤100n,m\leq 100n,m≤100,ai≤5a_i\leq 5ai≤5。
对于所有数据,n,m≤1000n,m\leq 1000n,m≤1000,1≤l≤r≤n1\leq l\leq r\leq n1≤l≤r≤n,0≤ai≤10000\leq a_i\leq 10000≤ai≤1000。
题目解析
这道题要求我们对给定序列区间 [l, r]
求其 mex
(即不在该区间内的最小非负整数)。mex
是序列中不包含的最小整数。例如,给定一个序列 1, 2, 3, 4
,其 mex
为 0
,因为 0
是最小的且不在这个序列中。
解题思路
-
暴力解法:
- 对每一个查询区间
[l, r]
,我们可以直接扫描区间[l, r]
,找到其中所有的数,记录这些数。 - 然后从
0
开始,找到最小的一个没有出现在区间内的数,返回这个数作为mex
。
由于题目中的
n
和m
最大为 1000,所以暴力方法的时间复杂度是O(n * m)
,每次查询的时间复杂度是O(n)
。最坏情况下,总时间复杂度为O(n * m)
,在最坏情况下(n = 1000, m = 1000
)是可以接受的。 - 对每一个查询区间
具体实现
-
读取输入数据。
-
处理每个查询,对于每个查询区间
[l, r]
,我们:-
利用一个数组
count
来记录区间中各个数值的出现情况。 -
找到最小的非负整数
mex
,它不在区间内出现。
-
#include
#include
#include
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; i++) {cin >> a[i];
}// 处理每次查询
for (int i = 0; i < m; i++) {int l, r;cin >> l >> r;l--; r--; // 转换为0-index// 用一个set来记录区间内的所有数set<int> nums_in_range;for (int j = l; j <= r; j++) {nums_in_range.insert(a[j]);}// 找到mexint mex = 0;while (nums_in_range.count(mex)) {mex++;}cout << mex << endl;
}return 0;
}
代码解析
- 输入处理:
- 首先读取
n
和m
。 - 然后读取长度为
n
的序列a
。
- 首先读取
- 查询处理:
- 对于每个查询区间
[l, r]
,我们通过set<int> nums_in_range
来记录该区间中所有不同的元素。 - 通过遍历区间
[l, r]
,将区间中的所有数插入到nums_in_range
中(set
会自动去重)。
- 对于每个查询区间
- 计算
mex
:- 从
0
开始查找最小的没有出现的数,即mex
。我们使用count
方法来检查mex
是否出现在set
中,直到找到一个不在其中的mex
。
- 从
- 输出结果:
- 对于每次查询,输出对应的
mex
值。
- 对于每次查询,输出对应的
时间复杂度
- 对于每个查询,扫描区间的时间复杂度是
O(r - l + 1)
,而构建set
的时间复杂度是O(r - l + 1)
。 - 查找
mex
的时间复杂度是O(mex)
,但是mex
最大也不会超过区间中最大数值 + 1,所以它的时间复杂度是O(n)
(理论上最多为n
)。 - 因此,每个查询的时间复杂度为
O(n)
,总时间复杂度为O(n * m)
,这是可以接受的。
简单算法思路
- 遍历查询区间:对于每一个查询,我们需要遍历区间
[l, r]
,将该区间内的所有数保存到一个集合中。 - 计算
mex
:mex
是从0
开始,找到最小的一个不在集合中的数。
优化:直接使用一个数组来记录该区间内数字的出现情况,而不使用 set
。
#include
#include
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; i++) {cin >> a[i];
}// 处理每次查询
for (int i = 0; i < m; i++) {int l, r;cin >> l >> r;l--; r--; // 转换为0-index// 用一个数组记录区间内的数是否出现过bool appeared[1001] = {false};// 标记区间内的数for (int j = l; j <= r; j++) {appeared[a[j]] = true;}// 找到最小的没出现的数即为mexint mex = 0;while (appeared[mex]) {mex++;}cout << mex << endl;
}return 0;
}
代码解析
- 输入处理:
- 首先读取
n
和m
。 - 然后读取长度为
n
的序列a
。
- 首先读取
- 查询处理:
- 对于每个查询区间
[l, r]
,我们创建一个布尔数组appeared
,该数组用于标记区间内出现的数字。数组大小为 1001,涵盖了所有可能出现的数字(0 到 1000)。
- 对于每个查询区间
- 计算
mex
:- 对于每个区间
[l, r]
,我们将区间内的每个数对应的appeared
数组位置标记为true
。 - 然后从
0
开始,查找最小的没有被标记为true
的数字,那个数字就是mex
。
- 对于每个区间
- 输出结果:
- 对于每次查询,输出对应的
mex
值。
- 对于每次查询,输出对应的
时间复杂度分析
- 对于每个查询,我们需要扫描区间
[l, r]
,这需要O(r - l + 1)
的时间。最大时,区间长度为n
。 - 对于每个查询,我们还需要检查
appeared
数组的mex
值,最多需要检查 1001 个位置,时间复杂度是O(1001)
,即常数时间。 - 所以每个查询的时间复杂度为
O(n)
,总时间复杂度为O(n * m)
。