📜  给定数组中所有无序对的按位或(1)

📅  最后修改于: 2023-12-03 15:11:40.341000             🧑  作者: Mango

给定数组中所有无序对的按位或

该问题为一个经典的算法问题,题目要求计算给定数组中所有无序对的按位或。

问题描述

给定一个正整数数组 nums,计算所有满足 i < j 的无序对 (i, j) 的按位或值。

具体地,按位或指每一位都为 0 时为 0,否则为 1。

示例

输入:nums = [5,2,7]

输出:11

解释:所有无序对的按位或值为:

  • (5,2) = 5 | 2 = 7
  • (5,7) = 5 | 7 = 7
  • (2,7) = 2 | 7 = 7

所以答案为 7 | 7 | 7 = 11。

解法

该问题可以使用位运算和线段树两种方法来求解。

方法一:位运算

对于每一位,若存在两个数在该位上都为 1,则所有的无序对在该位上的按位或值均为 1,反之为 0。

实现时可以将所有数按照二进制位拆分,对于每一位分别进行统计。具体地,使用一个长度为 32 的数组 count 存储每一位上的 1 的个数,然后对于每一个数,统计其二进制中每一位是不是 1,若是则计入 count 数组中,最终遍历所有数,对于每一位上的 1 的个数 count[i],计算出该位上的所有无序对的按位或值为 count[i] * (n - count[i])。

时间复杂度:O(nlogn)

空间复杂度:O(1)

class Solution:
    def countPairs(self, nums: List[int]) -> int:
        res = 0
        for i in range(32):
            mask = 1 << i
            cnt = 0
            for num in nums:
                if num & mask:
                    cnt += 1
            res += cnt * (len(nums) - cnt)
        return res
方法二:线段树

线段树的核心思想是将区间分治,将整个区间划分为若干个不相交的子区间,并且用一个树状结构维护这些子区间的信息,以支持区间查询、单点修改等操作。

对于该问题,我们可以将区间按照二进制位上是否相同来分治,具体地,用数组 tree[i][0/1] 表示第 i 位为 0/1 的数的个数,然后对于每一位上的 1,统计在该位上有多少个数是 0,将其与该位上有多少个数是 1 的数相乘,对于所有位上的值求和即为所求。

时间复杂度:O(nlogk),其中 k 为数组中元素的最大值。

空间复杂度:O(nlogk)

class Node:
    def __init__(self, l, r):
        self.l, self.r = l, r
        self.zero, self.one = 0, 0
        self.left, self.right = None, None
        
class SegmentTree:
    def __init__(self, nums):
        def build(l, r):
            if l > r:
                return None
            node = Node(l, r)
            if l == r:
                node.zero, node.one = 1, 0 if nums[l] else 1
            else:
                mid = (l + r) // 2
                node.left = build(l, mid)
                node.right = build(mid+1, r)
                node.zero = node.left.zero + node.right.zero
                node.one = node.left.one + node.right.one
            return node
        self.root = build(0, len(nums)-1)
        
    def query(self, l, r):
        def dfs(node):
            if not node:
                return 0, 0
            if node.r < l or node.l > r:
                return 0, 0
            if l <= node.l and node.r <= r:
                return node.zero, node.one
            zero1, one1 = dfs(node.left)
            zero2, one2 = dfs(node.right)
            return zero1+zero2, one1+one2
        return dfs(self.root)
    
class Solution:
    def countPairs(self, nums: List[int]) -> int:
        st = SegmentTree(nums)
        res = 0
        for i in range(32):
            mask = 1 << i
            cnt1, cnt2 = 0, 0
            for num in nums:
                if num & mask:
                    cnt1 += 1
                else:
                    cnt2 += 1
            num1, num2 = st.query(0, len(nums)-1)
            if cnt1 == 0:
                res += cnt2 * num2
            elif cnt2 == 0:
                res += cnt1 * num1
            else:
                res += cnt1 * num2 + cnt2 * num1
        return res//2