The 2026 ICPC Asia East Continent Online Contest (II) - K. K-MEX
目录
K. K-MEX
time limit per test: 1 second
memory limit per test: 512 megabytes
input: standard input
output: standard output
Let be an array of length . Define as the smallest non-negative integer that does not appear in . For example:
Define a -transformation as follows: for each index (), independently choose to either keep unchanged or replace it with . After all choices are made, we obtain a new array .
Define - as the maximum possible value of among all arrays that can be obtained from via a -transformation.
You must answer queries. Each query provides a non-negative integer ; compute - for each query.
To reduce the output size, you are only required to output the bitwise XOR (exclusive-OR) of the answers over all queries.
Input
The first line contains a single integer (), denoting the number of test cases.
For each test case:
- The first line contains a single positive integer (), denoting the length of the array.
- The second line contains integers (), denoting the initial elements of array .
- The next line contains a single positive integer (), denoting the number of queries.
- Each of the next lines contains a single non-negative integer (), denoting you must compute - for this .
It is guaranteed that and across all test cases in one test file.
Output
For each test case, output a single integer, denoting the bitwise XOR (exclusive-OR) of the answers to all queries in that test case.
Example
Input
2
3
0 1 3
3
1
2
5
9
0 0 1 1 2 3 3 4 5
7
4
5
6
7
8
9
10
Output
3
8
Note
For the first test case:
- For the query , replacing with for gives , whose is . Moreover, no -transformation can make appear in , so its cannot exceed . Therefore, -.
- For the query , the only element that can become either or is , so these two values cannot both appear in after any -transformation. Taking gives , so -.
- For the query , replacing with gives . Since the array has length , its cannot exceed . Therefore, -.
Thus, the bitwise XOR of the answers to the three queries is .
For the second test case, the answers to the queries are , respectively, and their bitwise XOR is .
题解
这道题是一道非常精妙的 MEX 性质分析 + 贪心匹配 的算法题。下面为你带来这道题的详细分析与题解。
一、 题目大意回顾
给定一个长度为 的非负整数数组 。
对于给定的整数 ,-变换允许我们将每个元素 独立地替换为 或 。
定义 为变换后数组 所能达到的最大 值。
有 次询问,每次给出一个 ,要求输出所有询问答案的异或和(XOR sum)。
数据范围:,。
二、 核心性质与突破口
性质 1:答案的天然下界
如果不做任何变换(即对所有 都保留 ),数组的 就是原数组的 。
设原数组的 为 。
那么无论 是多少,我们至少能达到 :
性质 2:绝大多数 的答案都严格等于 (最核心剪枝)
根据 MEX 的定义:
- 在原数组 中,数 都存在。
- 数 在原数组中不存在。
如果要使得变换后的 ,那么 数 必须出现在变换后的数组 中。
因为原数组中没有任何数等于 ,所以 只能通过某个数变换得到:
重要结论:
如果某个 不能写成 (其中 )的形式,那么无论怎么选择变换, 都绝对不可能出现在新数组中!
这意味着 绝不可能超过 。
结合性质 1,我们得出:对于所有不属于 的 ,其答案必然严格等于 !
因为数组长度为 ,所以可能让答案大于 的 最多只有 个!
对于其它所有询问,答案直接就是 。
性质 3:MEX 的理论上限
数组长度为 。一个长度为 的数组最多包含 个不同的数。
由鸽巢原理, 中至少有一个数不在数组中,因此:
所以我们只需要关心 范围内的数是否能被覆盖,大于等于 的数对提高 MEX 毫无用处。
三、 单个候选 的贪心求解
对于某个候选的 (形如 ),如何求出最大的 ?
1. 独立等价类分析
每个元素 只能变成 或 。
notice 到:
- 值为 的元素只能变成 或 。
- 值为 的元素只能变成 或 。
- 它们完全无法变成其它任何值!
也就是说,所有数值自动划分成了独立的数对 :
- 属于数对 的元素总数是固定的。
- 要覆盖 ,需要从该数对中至少消耗 1 个元素。
- 要同时覆盖 和 (当 时),需要该数对中至少有 2 个元素。
2. 贪心策略
由于我们希望 MEX 尽量大,目标是按顺序依次凑出 :
- 先统计能为 提供贡献的元素数量,存入计数数组
c:- 若 ,它本身就可以作为 ,放入
c[v]++; - 若 但 ,它虽然自己用不上,但可以变成 ,放入
c[k - v]++。
- 若 ,它本身就可以作为 ,放入
- 从 开始递增检查:
- 能否凑出 ?
- 首先看
c[x]是否 (使用本身就是 的数,或由 变换过来的数); - 若
c[x] == 0,再看c[k - x]是否 (将原本在 范围内的 翻转为 ); - 若两者都没有,说明无论如何也凑不出 ,循环终止,当前 即为最大 MEX;
- 若凑出了 ,将对应计数减 ,然后继续检验 。
- 首先看
- 能否凑出 ?
为什么这个贪心是正确的?
因为当我们检验较小的数 时,它与 共享同一个元素池。任何属于这个池的元素都可以当 或当 用。无论消耗哪一个,池子剩余的元素数量都是一样的,留给后续 的机会也是完全相等的。所以贪心地优先满足更小的数 是全局最优的。
四、 查询的异或性质技巧
题目要求对所有查询的答案求异或和:
- 异或满足自反性:。
- 如果同一个 出现了偶数次,它们的结果相互抵消为 。
- 因此题解代码中使用了
set<int> sq:
这样只需保留出现奇数次的 。auto [it, inserted] = sq.insert(K); if (!inserted) { sq.erase(it); // 如果已经存在,说明出现了偶数次,直接删除抵消 }
最终计算异或和:
- 如果 在预计算的候选字典
f中,异或其真实算出的最大 MEX; - 如果不在
f中,由性质 2 可知答案就是原数组的mex,直接异或mex。
五、 核心代码逐段详解
#include <bits/stdc++.h>
namespace rgs = std::ranges;
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int N;
cin >> N;
map<int, int> cnt;
vector<int> A;
for (int i = 0; i < N; i++) {
int x;
cin >> x;
cnt[x]++;
A.push_back(x);
}
rgs::sort(A);
int Q;
cin >> Q;
// 利用 set 去除偶数次重复的查询(偶数次异或为0)
set<int> sq;
while (Q--) {
int K;
cin >> K;
auto [it, inserted] = sq.insert(K);
if (!inserted) {
sq.erase(it);
}
}
vector<int> queries(sq.begin(), sq.end());
// 1. 求出原数组的 mex
int mex = 0;
while (cnt.contains(mex)) {
mex++;
}
// 2. 仅对 k = mex + v (v in A) 进行计算
map<int, int> f;
for (auto v : A) {
int k = mex + v;
if (f.contains(k)) continue; // 避免重复计算相同的 k
vector<int> c(N);
// 统计能贡献到 [0, N-1] 的元素
for (auto val : A) {
if (val < N) {
c[val]++;
} else if (k - val >= 0 && k - val < N) {
c[k - val]++;
}
}
// 贪心检验能连续凑出多大的数
int x = 0;
while (true) {
if (x < N && c[x]) {
c[x]--;
} else if (k - x >= 0 && k - x < N && c[k - x]) {
c[k - x]--;
} else {
break; // 凑不出 x,停止
}
x++;
}
f[k] = x;
}
// 3. 统计异或和
int ans = 0;
for (auto K : queries) {
if (f.contains(K)) {
ans ^= f[K]; // 特殊候选 k 的答案
} else {
ans ^= mex; // 其他 k 的答案必定为原数组的 mex
}
}
cout << ans << "\n";
}
return 0;
}
六、 复杂度分析
-
预处理与候选 的计算:
- 候选 最多有 个。
- 对每个候选 ,初始化
c数组耗时 ,贪心循环最多执行 次,耗时 。 - 总体候选计算时间为 。
- 数据保证 ,最坏情况下 ,在 C++ 中耗时仅几十毫秒。
-
询问处理:
- 使用
std::set去重抵消奇偶次数:。 - 查询答案并异或:。
- 数据保证 ,运算次数在 级别,耗时约 秒。
- 使用
总时间复杂度:,完全能在 1.0 秒的时限内轻松通过。
空间复杂度:,空间消耗极小。