[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)。