CF题解——Trip to the Olympiad

本文最后更新于 9 个月前,文中所描述的信息可能已发生改变。

C. Trip to the Olympiad 解题思路

核心目标与异或性质分析

本题要求在给定范围 [L,R][L, R] 内,找到三个互不相同的数字 a,b,ca, b, c,使得目标和 Sum=(ab)+(bc)+(ca)\text{Sum} = (a \oplus b) + (b \oplus c) + (c \oplus a) 最大。

我们首先分析异或和的性质。对于任意一个二进制位 ii,它对总和 Sum\text{Sum} 的贡献是 $ 2^i$ 乘以该位上三个异或结果 (ab)i,(bc)i,(ca)i(a \oplus b)_i, (b \oplus c)_i, (c \oplus a)_i 之和。

在一个特定的位 ii 上,数字 a,b,ca, b, c 的位值(bit values)只有四种组合会产生非零贡献:

  1. (0,0,1)(0, 0, 1) 或其排列: 此时 (ab)i=0,(bc)i=1,(ca)i=1(a \oplus b)_i = 0, (b \oplus c)_i = 1, (c \oplus a)_i = 1。总和为 $ 0 + 1 + 1 = 2$。
  2. (1,1,0)(1, 1, 0) 或其排列: 此时 (ab)i=0,(bc)i=1,(ca)i=1(a \oplus b)_i = 0, (b \oplus c)_i = 1, (c \oplus a)_i = 1。总和为 $ 0 + 1 + 1 = 2$。
  3. (0,0,0)(0, 0, 0)(1,1,1)(1, 1, 1) 此时所有异或结果均为 $ 0$。总和为 $ 0$。

因此,要使总和 Sum\text{Sum} 最大,我们应该尽量使高位的三个数字在该位上呈现 (0,0,1)(0, 0, 1)(1,1,0)(1, 1, 0) 的模式,以保证每一位都能贡献最大值 $ 2 \cdot 2^i$

寻找关键位 kk

quot;">​

由于 a,b,ca, b, c 必须在 [L,R][L, R] 范围内,我们不能随意构造数字。最大的挑战在于范围约束。

我们考虑范围的两个端点 LLRR。它们之间的最高不同位是限制我们能构造出多少高位 $ 1$ 的关键。

  1. 计算 LRL \oplus R 得到一个数,其最高位 kk 标志着 LLRR 在 $ 2^k$ 这一位上是不同的,而在所有更高的位( k+1,k+2,k+1, k+2, \dots )上是相同的。
  2. 确定关键位 kk kk 即为 LRL \oplus R 的最高设置位(Most Significant Bit, MSB)的索引。
  • 如果 L=RL = R,则 LR=0L \oplus R = 0,此时 k=0k=0(或 1-1,我们取 k=0k=0 作为安全底线)。
  • 如果 LRL \neq R,则 如果 LRL \neq R,则 kkLRL \oplus R 的最高设置位(MSB)索引,且 k>0k > 0

构造最优解 a,b,ca, b, c

quot;">​

在位 kk 以上,所有 [L,R][L, R] 范围内的数字都与 LLRR 相同。我们最大化目标和的潜力在于位 $ 0$ 到 位 kk。为了最大化贡献,我们希望 a,b,ca, b, c 在这 k+1k+1 位上实现全 $ 1$ 模式的最高贡献

我们构造两个数 aabb 如下:

  1. 构造 aa 我们取 LL 的高位部分,并在位 $ 0$ 到 位 k1k-1 上全部设置为 $ 1$。

    a=L((1k)1)a = L \mid ((1 \ll k) - 1)

    这样构造的 aa 满足 LaRL \le a \le R 的条件,因为它只在 LL 的低位上做了“或 $ 1$”操作,且 LLRR 在位 kk 上不同。

  2. 构造 bb 我们希望 bb 能实现与 aa 在位 kk 上的翻转,并使低位部分尽可能贡献 $ 1$。

    b=a+1b = a + 1

    分析 aabb

    • aa 在位 kk 以下全为 $ 1$。
    • b=a+1b = a+1 将使位 kk 以下全变为 $ 0$,并使位 kk 上的值从 aa 的值翻转(即 akbka_k \neq b_k)。
    • 由于 aa 是由 LL 通过“或 $ 1$”操作得到的,它必然满足 LaRL \le a \le R
    • b=a+1b = a+1 必然满足 LbRL \le b \le R。这是因为 RR 的高位部分与 LL 相同,但 RR 的位 kk 是 $ 1$,而 LL 的位 kk 是 $ 0a$ 和 bb 相当于取到了这个范围内的“最大跨度”数字。
  3. 构造 cc 既然 aabb 已经实现了在关键位 kk 上的翻转,以及在所有低位上对 Sum\text{Sum} 的最大贡献(通过 aak1k-1 位以下全是 $ 1$),剩下的第三个数字 cc 只需要满足:

    • LcRL \le c \le R
    • cac \neq acbc \neq b(题目要求互不相同,但实际上对于最大化 Sum\text{Sum} 的影响不大,只要有三个数能达到最大 Sum\text{Sum} 即可)。

    最简单的取法是取 c=Lc = Lc=Rc = R,只要它不与 aabb 重复即可。由于范围 [L,R][L, R] 至少包含 $ 3$ 个数,我们可以保证找到一个不重复的 cc

    例如,我们简单取 c=(a==L?R:L)c = (a == L ? R : L)

CPP 代码实现

cpp
#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

// 函数:获取最高设置位的索引
int get_msb_index(i64 n) {
    if (n == 0) {
        return -1;
    }

    // 从 63 位开始向下查找第一个 1
    for (int i = 63; i >= 0; --i) {
        if (n & (1LL << i)) {
            return i;
        }
    }
    return -1; // 理论上 n != 0 不会走到这里
}


void solve() {

    i64 l, r;
    cin >> l >> r;

    // 1. 找出 L 和 R 的最高不同位 k
    i64 xor_result = l ^ r;
    int k = get_msb_index(xor_result);

    // 边界情况:如果 L = R,则 k = -1。
    if (k < 0) {
        k = 0; // 此时 k 不影响后续 mask 的构造,但保证 a 至少是 l
    }

    // 2. 构造 mask,即 k 位以下的 1 ( 2^k - 1 )
    i64 mask = (1LL << k) - 1;

    // 3. 构造 a: L 的高位部分 + k 位以下全 1
    i64 a = l | mask;

    // 4. 构造 b: a + 1。
    i64 b = a + 1;

    // 5. 构造 c: 随便取 L 或 R 中与 a 不重复的一个。
    i64 c = (a == l ? r : l);

    // 实际验证:对于 L=99, R=109。
    // L = (01100011)_2, R = (01101101)_2
    // L ^ R = (00001110)_2. MSB k=3. mask =(00000111)_2 = 7.
    // a = L | 7 = 99 | 7 = 103. b = 104. c = 109.
    // (103 ^ 104) + (104 ^ 109) + (109 ^ 103) = 7 + 13 + 10 = 30.
    // 这是最大值。

    cout << a << " " << b << " " << c << "\n";
}

int main() {

    // 优化 I/O
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int test;
    cin >> test;

    while (test--) {
        solve();
    }
}
CF题解——Power of Points
“如何连接IPv6的服务器”