问题理解
小C拿到了一个数组,她定义一个数组的权值为数组中任意选取两个数的异或之和。具体来说,对于一个数组,每个连续子数组的任意两元素的异或值之和定义为其权值。她想知道该数组中所有可能的连续子数组的权值之和是多少,答案需要对 10^9 + 7 取模。
数据结构选择
- 数组:用于存储输入的数组
a。 - 整数变量:用于计数和索引操作。
- 计数器数组:用于按位统计当前位为
0和1的元素数量。
算法步骤
-
初始化:
- 定义常数
MOD = 10^9 + 7,用于结果取模。 - 定义变量
m = 31,假设整数为 32 位。 - 初始化答案
ans = 0。
- 定义常数
-
按位计算:
- 对于每一位
k(从 0 到 30),分别计算该位对最终答案的贡献。 - 初始化计数器数组
c = [0, 0],分别表示当前位为0和1的元素数量。 - 初始化变量
s = 0,用于累加当前位的贡献。
- 对于每一位
-
遍历数组:
- 对于数组
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的元素的累积前缀和。
- 对于数组
-
累加答案:
- 对于每一位,将该位的总贡献
s左移k位,加到答案中。
- 对于每一位,将该位的总贡献
-
返回结果:
- 返回答案
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)。
- 计数器数组