1 条题解

  • 0
    @ 2026-4-13 19:49:02

    C. XOR-distance 题解


    题意回顾

    给定三个整数 a,b,ra, b, r,要求在 0xr0 \le x \le r 的范围内选取 xx,使得 (ax)(bx)|(a \oplus x) - (b \oplus x)| 达到最小,并输出该最小值。


    问题转化

    f(x)=(ax)(bx)f(x) = |(a \oplus x) - (b \oplus x)|。由于异或运算是按位独立的,我们可以逐位分析 xx 的取值对结果的影响。

    不妨设 aba \le b(若 a>ba > b 则交换两者,不影响答案)。对于某一位 ii

    • aabb 在第 ii 位上相同(同为 00 或同为 11),则无论 xx 该位取何值,异或后 axa \oplus xbxb \oplus x 的该位依然相同,差值在该位上不受影响。
    • aabb 在第 ii 位上不同,则必定一个是 00 一个是 11。由于 aba \le b,在最高的不同位上,一定是 aa 的该位为 00bb 的该位为 11。在该位异或 xx 的对应位会同时翻转两个数的该位:原本为 00 的变成 11,为 11 的变成 00。因此,若 xx 的该位为 11,则 aabb 在该位上的大小关系会反转(aa 的位变成 11bb 变成 00),这会使 bab-a 减少 2i2^i;若 xx 的该位为 00,则保持原状,差值不变。

    我们的目标是最小化 bab - a(因为 aba \le b 且异或后两者大小关系可能变化,但绝对值保证了非负)。因此,我们希望在某些不同位上通过令 xi=1x_i = 1 来缩小差距,同时要满足 xrx \le r 的限制。


    贪心策略

    从最高位向最低位考虑(例如从第 5959 位到第 00 位):

    1. 第一个不同的位
      这是 aabb 的最高不同位。在这一位上,aa00bb11。如果我们令 xx 的这一位为 11,会使得原本较大的 bb 变小、较小的 aa 变大,可能导致 a>ba > b,从而差值变为 aba - b,但这并不一定比保持原样更优。事实上,保持第一个不同位的原状可以保留 b>ab > a 的“优势”,使得后续有更多的机会通过翻转其他位来减小差距。因此,对于最高不同位,我们选择不翻转(xx 该位取 00)。

    2. 后续的不同位
      对于之后遇到的每一位 ii,如果 aabb 在该位不同,则由于高位已经确定大小关系,此时 aa 的该位仍为 00bb11。如果我们令 xx 的这一位为 11,则 aa 增加 2i2^ibb 减少 2i2^i,总差值减少 2i+12^{i+1}
      显然,只要加上这一位的 2i2^ixx 仍然不超过 rr,我们就应该执行这一翻转,因为它严格减小最终差值且不会破坏高位已经建立的大小关系(因为高位不同位的取值已经固定,低位翻转不会改变 aba \le b 的大局)。如果翻转会导致 x>rx > r,则不能进行,只能取 00

    这种贪心选择能够保证在限制 rr 下差值尽可能小。正确性基于:每一位的贡献是独立的,且高位翻转的影响远大于所有低位翻转的总和(因为 2i>j=0i12j2^i > \sum_{j=0}^{i-1} 2^j)。因此,在第一个不同位之后,只要有机会减小差值且不超限,就立即执行是最优的。


    算法步骤

    1. 读入 a,b,ra, b, r
    2. a>ba > b,交换 a,ba, b,使 aba \le b
    3. 初始化 x=0x = 0,标志变量 first_bit = true 表示尚未遇到第一个不同位。
    4. 从高位到低位遍历(例如 i=59i = 5900):
      • 获取 aabb 在第 ii 位的值。
      • 若两值不同:
        • 若是第一个不同位,则将 first_bit 置为 false,不做其他操作。
        • 若不是第一个不同位,且 aa 的该位为 00(即 bb 的该位为 11),并且 x+2irx + 2^i \le r,则:
          • xx+2ix \gets x + 2^i
          • 同时将 aabb 的该位翻转(a ^= (1<<i), b ^= (1<<i)),以模拟 xx 的影响,便于后续计算差值。
    5. 输出 bab - a

    时间复杂度:每个测试用例 O(logmax(a,b,r))O(60)O(\log \max(a,b,r)) \approx O(60),总复杂度 O(t60)O(t \cdot 60),轻松通过。


    参考代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxb = 60;
    
    bool get_bit(int64_t a, int i) {
        return a & (1ll << i);
    }
    
    int main() {
        ios_base::sync_with_stdio(false);
        cin.tie(nullptr);
        int t;
        cin >> t;
        while (t--) {
            int64_t a, b, r;
            cin >> a >> b >> r;
            int64_t x = 0;
            bool first_bit = true;
            if (a > b)
                swap(a, b);
            for (int i = maxb - 1; i >= 0; --i) {
                bool bit_a = get_bit(a, i);
                bool bit_b = get_bit(b, i);
                if (bit_a != bit_b) {
                    if (first_bit) {
                        first_bit = false;
                    } else {
                        if (!bit_a && x + (1ll << i) <= r) {
                            x += (1ll << i);
                            a ^= (1ll << i);
                            b ^= (1ll << i);
                        }
                    }
                }
            }
            cout << b - a << "\n";
        }
        return 0;
    }
    

    细节说明

    • 由于 a,b,r1018<260a, b, r \le 10^{18} < 2^{60},故最高位只需考虑到第 5959 位即可。
    • 代码中 x 的累加用于判断是否超过 rr,但并不需要真正输出 xx,因此可以省略对 a,ba, b 的实际异或操作,直接维护当前差值并计算潜在收益,但直接修改 a,ba, b 亦无妨,逻辑更直观。
    • a,ba, b 在初始时高位就有多个不同位,第一个不同位不处理,后续按贪心尽可能翻转,最终差值即为答案。
    • 1

    信息

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