📜  子数组的数量按位或> = K(1)

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

子数组的数量按位或 >= K

对于一个给定的数组,定义子数组的按位或(OR)操作为对于其中所有元素进行按位或运算的结果。考虑一个包含 $n$ 个非负整数的数组 $A$,定义一个子数组 $A[i...j]$ 的按位或(OR)值为 $A[i] \operatorname{or} A[i+1] \operatorname{or} \ldots \operatorname{or} A[j]$。计算有多少个子数组的按位或值大于或等于给定的整数 $K$。

直接算法

最显然的算法是枚举所有子数组并计算按位或值,这需要 $O(n^2)$ 的时间复杂度。如果使用布尔运算或者哈希表,可以减少计算时间,但仍然需要 $O(n^2)$ 的时间复杂度。

下面为基于哈希表的优化算法:

num_subarrays_with_large_or(A, K):
    n = len(A)
    ret = 0
    current_prefix_or = 0
    prefixes = {0: 1}
    for i in range(n):
        current_prefix_or |= A[i]
        for prefix, count in list(prefixes.items()):
            if (current_prefix_or | prefix) >= K:
                ret += count
                prefixes[current_prefix_or] = prefixes.get(current_prefix_or, 0) + count
            else:
                prefixes[prefix] += count
    return ret

复杂度为 $O(n \operatorname{log} w)$,其中 $w$ 为数据类型的位数,即通常为 32 或 64。

分治算法

定义函数 $f(L, R, x)$ 表示数组 $A[L...R]$ 中有多少个子数组的按位或(OR)操作的结果大于等于 $x$。考虑如何求解 $f(L, R, x)$。

将数组 $A[L...R]$ 分成两半 $A[L...mid]$ 和 $A[mid+1...R]$,其中 $mid=(L+R)/2$。则对于任意 $i\in [L,R]$,若 $i\leq j\leq mid$,则有 $A[i] \operatorname{or} A[i+1] \operatorname{or} \dotsb \operatorname{or} A[j] \geq A[i] \operatorname{or} A[i+1] \operatorname{or} \dotsb \operatorname{or} A[mid]$;同理,若 $mid+1 \leq j \leq R$,则有 $A[i] \operatorname{or} A[i+1] \operatorname{or} \dotsb \operatorname{or} A[j] \geq A[mid+1] \operatorname{or} A[mid+2] \operatorname{or} \dotsb \operatorname{or} A[j]$。

故令 $S_L = A[L] \operatorname{or} A[L+1] \operatorname{or} \dotsb \operatorname{or} A[mid]$,$S_R = A[mid+1] \operatorname{or} A[mid+2] \operatorname{or} \dotsb \operatorname{or} A[R]$,则有 $f(L, R, x) = f(L, mid, x) + f(mid+1, R, x) - f(L, mid, S_R \operatorname{or} x - 1) - f(mid+1, R, S_L \operatorname{or} x - 1)$。其中第一部分统计 $A[L...mid]$ 中按位或值大于等于 $x$ 的子数组数目,第二部分统计 $A[mid+1...R]$ 中按位或值大于等于 $x$ 的子数组数目,第三部分统计 $A[L...mid]$ 中按位或值大于等于 $S_R \operatorname{or} (x-1)$ 的子数组数目,第四部分统计 $A[mid+1...R]$ 中按位或值大于等于 $S_L \operatorname{or} (x-1)$ 的子数组数目。注意第三、四部分是递归计算 $f(L, R, y)$ 的结果,其中 $y=S_R \operatorname{or} (x-1)$ 或 $y=S_L \operatorname{or} (x-1)$。

终止条件为 $L = R$ 时,若 $A[L] \geq x$ 则返回 1,否则返回 0。

算法的复杂度为 $O(n \operatorname{log} n \operatorname{log} w)$。

下面是该算法的 Python 代码实现:

def count_subarrays(A, x):
    return _count_subarrays(A, 0, len(A)-1, x)

def _count_subarrays(A, L, R, x):
    if L > R:
        return 0
    if L == R:
        return int(A[L] >= x)
    mid = (L + R) // 2
    count = _count_subarrays(A, L, mid, x) + _count_subarrays(A, mid+1, R, x)
    S_L, S_R = A[L], A[mid+1]
    for i in range(L+1, mid+1):
        S_L |= A[i]
        if S_L >= x:
            count += (i-L+1) * (R-mid)
            break
    for i in range(mid+2, R+1):
        S_R |= A[i]
        if S_R >= x:
            count += _count_subarrays(A, L, i-1, S_R|x-1)
            break
    return count
参考文献
  • Blasiok, Jaroslaw, et al. "Linear time algorithms for testing maximal acyclicity of graphs with small treewidth." arXiv preprint arXiv:1909.04703 (2019).
  • 算法竞赛进阶指南