《小E的区间异或和》 | 豆包MarsCode AI刷题

52 阅读2分钟

问题理解

小C拿到了一个数组,她定义一个数组的权值为数组中任意选取两个数的异或之和。具体来说,对于一个数组,每个连续子数组的任意两元素的异或值之和定义为其权值。她想知道该数组中所有可能的连续子数组的权值之和是多少,答案需要对 10^9 + 7 取模。

数据结构选择

  • 数组:用于存储输入的数组 a
  • 整数变量:用于计数和索引操作。
  • 计数器数组:用于按位统计当前位为 01 的元素数量。

算法步骤

  1. 初始化

    • 定义常数 MOD = 10^9 + 7,用于结果取模。
    • 定义变量 m = 31,假设整数为 32 位。
    • 初始化答案 ans = 0
  2. 按位计算

    • 对于每一位 k(从 0 到 30),分别计算该位对最终答案的贡献。
    • 初始化计数器数组 c = [0, 0],分别表示当前位为 01 的元素数量。
    • 初始化变量 s = 0,用于累加当前位的贡献。
  3. 遍历数组

    • 对于数组 a 中的每个元素 x,取出第 k 位的值 b = (x >> k) & 1
    • 累加当前位的贡献:
      • 当前元素可以与之前所有第 k 位为 1 - b 的元素形成一对,它们的异或在第 k 位会产生 1,并且从当前元素 x 开始的所有可能的连续子数组中,这些对都会贡献异或结果。
      • 贡献值为 c[1 - b] * (n - j),其中 n - j 表示从位置 j 开始到数组末尾的子数组数量。
    • 更新计数器:
      • c[b] += j + 1,表示第 k 位为 b 的元素的累积前缀和。
  4. 累加答案

    • 对于每一位,将该位的总贡献 s 左移 k 位,加到答案中。
  5. 返回结果

    • 返回答案 ans

代码实现

def solution(n: int, a: list) -> int:
    assert n == len(a)
    mod = int(1e9 + 7)
    m = 31  # 假设整数为 32 位
    ans = 0
    for k in range(m):
        c = [0, 0]  # c[0]: 第k位为0的计数;c[1]: 第k位为1的计数
        s = 0
        for j, x in enumerate(a):
            b = (x >> k) & 1
            # 累加当前位的贡献
            s = (s + c[1 - b] * (n - j)) % mod
            # 更新计数器
            c[b] = (c[b] + j + 1) % mod
        # 将第k位的总贡献左移k位,加入答案
        ans = (ans + (s << k)) % mod
    return ans

时间和空间复杂度

  • 时间复杂度O(n * m),其中 n 为数组长度,m 为整数的位数(假设为 32 位)。

    • 遍历数组的每一位需要 O(n),每一位的计算需要 O(m)
    • 总时间复杂度为 O(n * m)
  • 空间复杂度O(1),仅使用了常数级别的额外空间。

    • 计数器数组 c 和累加变量 s 均为常数空间。
    • 总空间复杂度为 O(1)