小K的区间与值和| 豆包MarsCode AI刷题

54 阅读3分钟

一、问题描述

小K有一个数组,她定义数组的权值为数组中任选两个数的按位与的值之和。具体来说,对于数组中的每个连续子数组,我们可以计算所有可能的两个元素的按位与值之和,并将这些值相加。小K想知道该数组中所有可能的连续子数组的权值和是多少,最后结果对10^9 + 7取模。

输入:

  • 一个整数 n ,表示数组的长度。
  • 一个整数数组 a ,包含 n 个整数。

输出:

  • 数组中所有可能的连续子数组的权值和,对 10^9 + 7 取模。

二、思路分析

1. 暴力解法的不可行性

最直接的想法是对于每个可能的连续子数组,计算其中所有元素对的按位与之和,然后累加。然而,这种方法的时间复杂度是 O(n3)O(n^3)O(n3),对于大的 nnn 值(例如 n=105n = 10^5n=105),计算量过大,无法在合理时间内完成。

2. 优化思路

为了优化时间复杂度,我们需要找到一种更高效的计算方法。注意到按位与运算的性质,以及元素在连续子数组中出现的规律,我们可以从以下几个方面入手:

  • 元素对出现的次数:找出每一对元素在所有连续子数组中共同出现的次数。
  • 按位与的贡献:计算每一对元素的按位与值乘以其出现次数,累加得到最终结果。

3. 元素对出现次数的计算

对于数组中的任意一对元素 a[p] 和 a[q](其中 p≤q),它们在所有连续子数组中共同出现的次数为:

次数=(p+1)×(n−q)

  • 起始位置选择:子数组的起始位置可以从索引 0 到 p,共有 p+1 种选择。
  • 结束位置选择:子数组的结束位置可以从索引 q 到 n−1,共有 n−q 种选择。

4. 算法步骤

  1. 初始化结果变量,用于累加总权值,取模 10^9 + 7。
  2. 遍历所有可能的元素对(只需遍历 p≤q 的情况,避免重复计算)。
  3. 计算元素对的贡献次数,根据上述公式。
  4. 累加按位与的贡献,即 (a[p]&a[q])×次数(a[p] & a[q]) 。
  5. 返回结果

三、代码实现

def solution(n: int, a: list) -> int:
    result = 0
    MOD = 10**9 + 7
    
    for p in range(n):
        for q in range(p+1, n):
            #计算p, q在多少个子数组中共同出现
            num_subarrays = (p + 1) * (n - q)
            #p, q这一组出现的次数乘按位与
            total_contrib = (a[p] & a[q]) * num_subarrays
            result += total_contrib
    
    return result%MOD

if __name__ == '__main__':
    print(solution(4, [2, 3, 1, 2])==16)
    print(solution(3, [5, 6, 7]) == 25)
    print(solution(2, [1, 10]) == 0)
    print(solution(5, [1, 2, 4, 8, 16]) == 0)

五、复杂度分析

  • 时间复杂度:O(n^2),因为需要遍历所有元素对。
  • 空间复杂度:O(1),只使用了常数额外空间。

虽然时间复杂度为 O(n^2),但对于 n 较小的情况(如 n103n \leq 10^3),仍能在合理时间内运行。如果 n 较大,需要进一步优化。

八、个人思考

在解决这个问题的过程中,我深刻体会到了算法优化的重要性。最初的暴力解法虽然直观,但在面对大数据时完全不可行。通过分析按位与的性质和元素在子数组中出现的规律,我们找到了更高效的解法。

对于初学者,我的建议是多练习、多思考。当你遇到一个问题时,不仅要想着如何解决,还要思考是否有更好的方法。善于利用数据结构和算法的特性,可以让你的代码更高效、更优雅。