1 条题解
-
0
题目大意
给定一个长度为 的数组 ()。
对于任意长度至少为 的子数组 ,定义其“值”为子数组中所有不同位置元素异或的最小值,即现在要找出所有这样的子数组(共 个)的值,将它们从小到大排序后,求第 小的值。
解题思路
1. 问题转化
直接枚举所有子数组并计算最小值是不可行的,因为 太大。
常见技巧是二分答案:设答案为 ,则我们需要判断有多少个子数组的“值” 。记这个数量为 。
若 ,则答案 ,否则答案 。
通过二分 可以将原问题转化为判定问题。二分上界:因为 ,所以任何两个数的异或最大不超过 ,可以取上界为 。
2. 判定问题:统计“值 ”的子数组个数
对于固定的 ,我们想要数出有多少个子数组满足其内部的最小异或值不超过 。
关键观察
对于一个子数组,若我们不断增加它的左端点(即删除左边元素),子数组的长度变小,最小异或值只会增大或不变(因为删除了某些数对,可能消除了较小的异或值)。
因此,对于固定的右端点 ,子数组 的最小异或值关于 是单调不减的。
这允许我们使用双指针(滑动窗口):- 维护一个窗口 ,并实时维护该窗口内所有元素两两异或的最小值。
- 当我们向右移动 时,将 加入窗口,窗口的最小异或值可能会变小。
- 接着,只要窗口的最小异或值 ,我们就将左端点 向右移动(删除 ),直到窗口的最小异或值 。
- 此时,对于当前右端点 ,所有以 为右端点且左端点小于 的子数组 ()都必然满足其最小异或值 。
理由: 包含了 中的元素,还多了左边的元素,所以它的最小异或值不会大于 的最小异或值,而 已经大于 ,因此更长的区间可能包含更小的异或值,我们需要的是 的区间。实际上,当窗口 的最小异或值 时,更长的区间()因为包含更多元素,可能产生更小的异或,所以可能 。因此所有 都满足条件,数量恰好为 。
对每个 累加 ,即得到 。
3. 动态维护窗口的最小异或值
我们需要一个数据结构,支持:
- 插入一个数
- 删除一个数
- 查询当前集合中任意两个数异或的最小值
一个重要结论:对于一个有序的整数集合,最小异或值一定出现在相邻元素之间。
证明:设 ,则 。因此只需维护排序后相邻两个数的异或值,取最小值即可。具体实现:
- 用一个
multiset(或平衡树)维护当前窗口中的所有数(按值排序)。 - 同时维护另一个
multiset存放相邻数的异或值。 - 插入 :找到它的前驱和后继,更新相邻异或值。
- 删除 :同样更新相邻异或值。
- 查询最小值:取第二个
multiset的最小值(若元素个数 则返回无穷大)。
每次操作 ,窗口滑动总复杂度 。
4. 算法流程
- 读入 和数组 (下标从 开始)。
- 二分答案 ,范围 :
- 计算 。
- 若 ,则 ,且 ;
- 否则 。
- 输出最终 。
时间复杂度:二分 次,每次判定 ,总 ,可承受 。
注意事项
- 可能达到 ,需要使用
long long类型。 - 数据范围保证所有测试用例的 之和 ,因此总体复杂度 可接受。
- 滑动窗口中的左指针 初始为 ,且 ,当 时窗口只有一个元素,此时不存在异或对,最小值设为无穷大。
- 实现时,相邻异或值集合需要支持重复值(因为异或结果可能相同),使用
multiset而不是set。
总结
本题的核心是利用单调性将最小值问题转化为判定问题,再通过滑动窗口 + 有序集合维护相邻异或值来高效统计。
该技巧在许多求“子数组最小异或值”或“子数组最小差值”的第 小问题中都可以使用。
- 1
信息
- ID
- 6933
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 8
- 已通过
- 1
- 上传者