1 条题解

  • 0
    @ 2025-12-10 21:58:27

    题解

    这道题要求我们找到一个最小的 bb,使得对数组进行异或变换后,数组变为非递减序列。我们需要在初始状态和每次单点修改后输出答案。

    问题转化

    首先,我们分析条件:

    $$(a[i] \oplus b) \leq (a[i+1] \oplus b) \quad \text{对所有} \ i=1,\dots,n-1 $$

    对于每对相邻元素 (a[i],a[i+1])(a[i], a[i+1]),我们需要找到满足条件的 bb。最终答案需要满足所有相邻对的约束。

    按位分析

    考虑两个数 xxyyx=a[i],y=a[i+1]x = a[i], y = a[i+1]),我们想知道 bb 应该满足什么条件才能使得 xbybx \oplus b \leq y \oplus b

    k=msb(xy)k = \text{msb}(x \oplus y),即 xxyy 的最高不同位。由于 kk 是最高不同位,我们可以得到:

    对于 xbx \oplus byby \oplus b 的比较,关键是看第 kk 位:

    • 如果 xx 的第 kk 位为 0,yy 的第 kk 位为 1,那么无论 bb 的第 kk 位是什么,xbx \oplus b 的第 kk 位都会小于等于 yby \oplus b 的第 kk 位,且高位相同
      • 如果 bk=0b_k = 0,则 (xb)k=0(x \oplus b)_k = 0(yb)k=1(y \oplus b)_k = 1
      • 如果 bk=1b_k = 1,则 (xb)k=1(x \oplus b)_k = 1(yb)k=0(y \oplus b)_k = 0
    • 如果 xx 的第 kk 位为 1,yy 的第 kk 位为 0,情况类似

    更精确地说:

    1. 如果 x<yx < y

      • k=msb(xy)k = \text{msb}(x \oplus y)
      • 如果 xx 的第 kk 位为 0,yy 的第 kk 位为 1
        • 那么需要 bb 的第 kk 位为 0(否则会反转大小关系)
      • 如果 xx 的第 kk 位为 1,yy 的第 kk 位为 0
        • 那么需要 bb 的第 kk 位为 1(否则会反转大小关系)
    2. 如果 x>yx > y

      • k=msb(xy)k = \text{msb}(x \oplus y)
      • 如果 xx 的第 kk 位为 0,yy 的第 kk 位为 1
        • 那么需要 bb 的第 kk 位为 1(这样才能使 xb>ybx \oplus b > y \oplus b
      • 如果 xx 的第 kk 位为 1,yy 的第 kk 位为 0
        • 那么需要 bb 的第 kk 位为 0(这样才能使 xb>ybx \oplus b > y \oplus b

    简化结论

    实际上,我们可以用更简洁的方式描述:

    对于相邻元素 a[i]a[i]a[i+1]a[i+1]

    • 如果 a[i]<a[i+1]a[i] < a[i+1]

      • k=msb(a[i]a[i+1])k = \text{msb}(a[i] \oplus a[i+1])
      • 如果 a[i]a[i] 的第 kk 位为 0,则 bb 的第 kk 位必须为 0
      • 如果 a[i]a[i] 的第 kk 位为 1,则 bb 的第 kk 位必须为 1

      等价于:bb 的第 kk 位必须等于 a[i]a[i] 的第 kk 位。

    • 如果 a[i]>a[i+1]a[i] > a[i+1]

      • k=msb(a[i]a[i+1])k = \text{msb}(a[i] \oplus a[i+1])
      • 如果 a[i]a[i] 的第 kk 位为 0,则 bb 的第 kk 位必须为 1
      • 如果 a[i]a[i] 的第 kk 位为 1,则 bb 的第 kk 位必须为 0

      等价于:bb 的第 kk 位必须等于 1a[i]1 - a[i] 的第 kk 位(即 a[i]a[i] 的第 kk 位取反)。

    • 如果 a[i]=a[i+1]a[i] = a[i+1]:对 bb 没有约束。

    算法设计

    根据上述分析,我们可以为每个二进制位 kk 维护两个计数器:

    • cnt_nd[k]:第 kk 位必须为 1 的约束数量
    • cnt_ban[k]:第 kk 位必须为 0 的约束数量

    初始构建

    遍历所有相邻对 (a[i],a[i+1])(a[i], a[i+1])

    • 如果 a[i]<a[i+1]a[i] < a[i+1]
      • k=msb(a[i]a[i+1])k = \text{msb}(a[i] \oplus a[i+1])
      • 如果 (a[i]>>k)&1=1(a[i] >> k) \& 1 = 1,则 cnt_nd[k]++(该位必须为 1)
      • 如果 (a[i]>>k)&1=0(a[i] >> k) \& 1 = 0,则 cnt_ban[k]++(该位必须为 0)
    • 如果 a[i]>a[i+1]a[i] > a[i+1]
      • k=msb(a[i]a[i+1])k = \text{msb}(a[i] \oplus a[i+1])
      • 如果 (a[i]>>k)&1=1(a[i] >> k) \& 1 = 1,则 cnt_ban[k]++(该位必须为 0)
      • 如果 (a[i]>>k)&1=0(a[i] >> k) \& 1 = 0,则 cnt_nd[k]++(该位必须为 1)
    • 如果 a[i]=a[i+1]a[i] = a[i+1]:不操作

    查询答案

    要找到最小的 bb,我们从高位到低位贪心地确定每一位:

    1. 如果某一位同时存在必须为 1 和必须为 0 的约束(即 cnt_nd[k] > 0cnt_ban[k] > 0),则无解,返回 -1
    2. 否则:
      • 如果存在必须为 1 的约束(cnt_nd[k] > 0),则该位必须为 1
      • 否则(cnt_nd[k] = 0):
        • 如果存在必须为 0 的约束(cnt_ban[k] > 0),则该位必须为 0
        • 否则,为了得到最小的 bb,该位设为 0

    注意:由于我们是从高位到低位确定,且在没有约束时设该位为 0,这样得到的就是最小的满足条件的 bb

    修改操作

    当修改位置 uu 的值时,只有与 uu 相关的相邻对会发生变化:

    • (a[u1],a[u])(a[u-1], a[u])(如果 u>1u > 1
    • (a[u],a[u+1])(a[u], a[u+1])(如果 u<nu < n

    我们需要:

    1. 移除旧值对应的约束
    2. 添加新值对应的约束
    3. 更新数组 aa 的值

    复杂度分析

    • 初始构建:O(nlogM)O(n \log M),其中 MM 是数值范围(2302^{30}
    • 每次查询:O(logM)O(\log M)
    • 每次修改:O(logM)O(\log M)
    • 总复杂度:O((n+q)logM)O((n+q) \log M)

    对于 n,q106n, q \leq 10^6logM=30\log M = 30,可以在时限内完成。

    正确性证明

    1. 必要性:如果 bb 是所有约束的解,那么它必须满足每个约束条件。
    2. 充分性:如果 bb 满足所有约束条件,那么对于任意相邻对 (a[i],a[i+1])(a[i], a[i+1]),考虑最高不同位 kkbb 的第 kk 位确保了 a[i]ba[i] \oplus ba[i+1]ba[i+1] \oplus b 的大小关系正确。
    3. 最小性:我们从高位到低位贪心地确定 bb,在没有约束时设为 0,这保证了得到的 bb 是最小的。

    代码实现细节

    // 检查相邻对并更新约束
    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;
    }
    

    总结

    本题的关键在于将相邻元素的大小关系约束转化为对 bb 的二进制位的约束。通过维护每个二进制位的约束计数器,我们可以在 O(logM)O(\log M) 的时间内回答查询,并支持 O(logM)O(\log M) 的单点修改。这是一个典型的位运算+约束满足问题,通过巧妙的位分析简化了问题。

    • 1

    信息

    ID
    5783
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者