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

57 阅读4分钟

问题描述

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

问题理解

我们需要计算一个数组中所有可能的连续子数组的权值之和。具体来说,权值定义为每个连续子数组中任意两元素的异或值之和。最终答案需要对 10^9 + 7 取模。

数据结构选择

由于涉及到异或操作,我们可以考虑按位来处理问题。每个元素的二进制表示可以帮助我们更好地理解和计算异或值。

算法步骤

  1. 按位处理:我们可以逐位(从低位到高位)来处理数组中的元素。对于每一位,我们计算所有可能的连续子数组在该位上的贡献。
  2. 统计贡献:对于每一位,我们需要统计在该位上为 1 和为 0 的元素的数量。通过这些统计信息,我们可以计算出所有可能的连续子数组在该位上的异或值之和。
  3. 累加结果:将每一位的贡献累加起来,最终得到整个数组的权值之和。

具体步骤

  1. 初始化:初始化一个变量 ans 用于存储最终结果,初始值为 0

  2. 逐位处理:对于每一位 k(从 0 到 31,因为 10^9 的二进制表示最多有 31 位):

    • 初始化两个计数器 c[0] 和 c[1],分别用于统计当前位为 0 和为 1 的元素数量。
    • 遍历数组中的每个元素,计算当前位上的贡献,并更新计数器。
    • 将当前位的贡献累加到 ans 中。
  3. 取模:在每次累加时,对结果取模 10^9 + 7,以防止溢出。

如何计算每个子数组的权值的?

代码分析

1732885752930.png

逐行解释

  1. 初始化

1732885812901.png

-   `assert n == len(a)`:确保输入的数组长度与 `n` 一致。
-   `mod = int(1e9 + 7)`:定义取模的值为 `10^9 + 7`-   `m = 31`:定义二进制位数为 31,因为 `10^9` 的二进制表示最多有 31 位。
-   `ans = 0`:初始化最终结果 `ans` 为 0。

2. 逐位处理

1732885862865.png

-   `for k in range(m)`:遍历每一位(从第 0 位到第 30 位)。
-   `c = [0, 0]`:初始化两个计数器 `c[0]` 和 `c[1]`,分别用于统计当前位为 `0` 和为 `1` 的元素数量。
-   `s = 0`:初始化当前位的贡献 `s` 为 0。

3. 遍历数组

1732885932236.png

-   `for j, x in enumerate(a)`:遍历数组中的每个元素 `x`,并记录其索引 `j`-   `b = (x >> k) & 1`:计算当前元素 `x` 在第 `k` 位上的值(0 或 1)。
-   `s = (s + c[1 - b] * (n - j)) % mod`:计算当前位的贡献。`c[1 - b]` 是当前位为 `1 - b` 的元素数量,`(n - j)` 是当前元素之后的元素数量。
-   `c[b] = (c[b] + j + 1) % mod`:更新当前位为 `b` 的元素数量。`j + 1` 表示当前元素及其之前的元素数量。

4. 累加结果

1732885967351.png - ans = (ans + (s << k)) % mod:将当前位的贡献 s 左移 k 位(相当于乘以 2^k),并累加到最终结果 ans 中。

  1. 返回结果

1732886000819.png - return ans:返回最终结果。

代码提示

1732886538983.png

关键步骤解释

  1. 初始化计数器

1732886232827.png

c = [0, 0]

-   `c[0]` 和 `c[1]` 分别用于统计当前位为 `0` 和 `1` 的元素数量。

2. 计算当前位的贡献

1732886291989.png - c[1 - b] 是当前位为 1 - b 的元素数量。 - (n - j) 是当前元素之后的元素数量。 - 这一步计算当前位的贡献,并累加到 s 中。

  1. 更新计数器

1732886352348.png

-   `c[b]` 是当前位为 `b` 的元素数量。
-   `j + 1` 表示当前元素及其之前的元素数量。
-   这一步更新当前位为 `b` 的元素数量。

4. 累加当前位的贡献到最终结果

1732886395638.png

-   `s << k` 将当前位的贡献 `s` 左移 `k` 位(相当于乘以 `2^k`)。
-   这一步将当前位的贡献累加到最终结果 `ans` 中。

总结

通过逐位处理数组中的元素,计算每个位上的贡献,并累加到最终结果中。具体步骤如下:

  1. 初始化结果和计数器。
  2. 逐位遍历数组中的元素,计算当前位的贡献。
  3. 更新计数器并累加当前位的贡献。
  4. 返回最终结果。