1 条题解
-
0
题解
这道题要求我们找到一个最小的 ,使得对数组进行异或变换后,数组变为非递减序列。我们需要在初始状态和每次单点修改后输出答案。
问题转化
首先,我们分析条件:
$$(a[i] \oplus b) \leq (a[i+1] \oplus b) \quad \text{对所有} \ i=1,\dots,n-1 $$对于每对相邻元素 ,我们需要找到满足条件的 。最终答案需要满足所有相邻对的约束。
按位分析
考虑两个数 和 (),我们想知道 应该满足什么条件才能使得 。
设 ,即 和 的最高不同位。由于 是最高不同位,我们可以得到:
对于 和 的比较,关键是看第 位:
- 如果 的第 位为 0, 的第 位为 1,那么无论 的第 位是什么, 的第 位都会小于等于 的第 位,且高位相同
- 如果 ,则 ,
- 如果 ,则 ,
- 如果 的第 位为 1, 的第 位为 0,情况类似
更精确地说:
-
如果 :
- 设
- 如果 的第 位为 0, 的第 位为 1
- 那么需要 的第 位为 0(否则会反转大小关系)
- 如果 的第 位为 1, 的第 位为 0
- 那么需要 的第 位为 1(否则会反转大小关系)
-
如果 :
- 设
- 如果 的第 位为 0, 的第 位为 1
- 那么需要 的第 位为 1(这样才能使 )
- 如果 的第 位为 1, 的第 位为 0
- 那么需要 的第 位为 0(这样才能使 )
简化结论
实际上,我们可以用更简洁的方式描述:
对于相邻元素 和 :
-
如果 :
- 设
- 如果 的第 位为 0,则 的第 位必须为 0
- 如果 的第 位为 1,则 的第 位必须为 1
等价于: 的第 位必须等于 的第 位。
-
如果 :
- 设
- 如果 的第 位为 0,则 的第 位必须为 1
- 如果 的第 位为 1,则 的第 位必须为 0
等价于: 的第 位必须等于 的第 位(即 的第 位取反)。
-
如果 :对 没有约束。
算法设计
根据上述分析,我们可以为每个二进制位 维护两个计数器:
cnt_nd[k]:第 位必须为 1 的约束数量cnt_ban[k]:第 位必须为 0 的约束数量
初始构建
遍历所有相邻对 :
- 如果 :
- 设
- 如果 ,则
cnt_nd[k]++(该位必须为 1) - 如果 ,则
cnt_ban[k]++(该位必须为 0)
- 如果 :
- 设
- 如果 ,则
cnt_ban[k]++(该位必须为 0) - 如果 ,则
cnt_nd[k]++(该位必须为 1)
- 如果 :不操作
查询答案
要找到最小的 ,我们从高位到低位贪心地确定每一位:
- 如果某一位同时存在必须为 1 和必须为 0 的约束(即
cnt_nd[k] > 0且cnt_ban[k] > 0),则无解,返回 -1 - 否则:
- 如果存在必须为 1 的约束(
cnt_nd[k] > 0),则该位必须为 1 - 否则(
cnt_nd[k] = 0):- 如果存在必须为 0 的约束(
cnt_ban[k] > 0),则该位必须为 0 - 否则,为了得到最小的 ,该位设为 0
- 如果存在必须为 0 的约束(
- 如果存在必须为 1 的约束(
注意:由于我们是从高位到低位确定,且在没有约束时设该位为 0,这样得到的就是最小的满足条件的 。
修改操作
当修改位置 的值时,只有与 相关的相邻对会发生变化:
- (如果 )
- (如果 )
我们需要:
- 移除旧值对应的约束
- 添加新值对应的约束
- 更新数组 的值
复杂度分析
- 初始构建:,其中 是数值范围()
- 每次查询:
- 每次修改:
- 总复杂度:
对于 ,,可以在时限内完成。
正确性证明
- 必要性:如果 是所有约束的解,那么它必须满足每个约束条件。
- 充分性:如果 满足所有约束条件,那么对于任意相邻对 ,考虑最高不同位 , 的第 位确保了 和 的大小关系正确。
- 最小性:我们从高位到低位贪心地确定 ,在没有约束时设为 0,这保证了得到的 是最小的。
代码实现细节
// 检查相邻对并更新约束 void chk(int a, int b) { if (a == b) return; int k = std::__lg(a ^ b); // 求最高不同位 if (a < b) { // a的第k位决定b的第k位 if ((a >> k) & 1) cnt_nd[k]++; // 必须为1 else cnt_ban[k]++; // 必须为0 } else { // a > b // a的第k位取反决定b的第k位 if ((a >> k) & 1) cnt_ban[k]++; // 必须为0 else cnt_nd[k]++; // 必须为1 } } // 移除约束 void unchk(int a, int b) { if (a == b) return; int k = std::__lg(a ^ b); if (a < b) { if ((a >> k) & 1) cnt_nd[k]--; else cnt_ban[k]--; } else { if ((a >> k) & 1) cnt_ban[k]--; else cnt_nd[k]--; } } // 查询答案 int get_ans() { int ans = 0; for (int i = 30; i >= 0; i--) { if (cnt_nd[i] && cnt_ban[i]) return -1; // 冲突 if (cnt_nd[i]) ans |= (1 << i); // 必须为1 // 否则:要么必须为0,要么无约束,都设为0以保证最小 } return ans; }总结
本题的关键在于将相邻元素的大小关系约束转化为对 的二进制位的约束。通过维护每个二进制位的约束计数器,我们可以在 的时间内回答查询,并支持 的单点修改。这是一个典型的位运算+约束满足问题,通过巧妙的位分析简化了问题。
- 如果 的第 位为 0, 的第 位为 1,那么无论 的第 位是什么, 的第 位都会小于等于 的第 位,且高位相同
- 1
信息
- ID
- 5783
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者