
推荐先阅读:从集合论到位运算,常见位运算技巧分类总结!
用位运算代替数组操作:
本质是模 剩余系的加法。
另见 数据结构题单 中的「§6.4 0-1 字典树(异或字典树)」。
的数越多,结果越小。
的数越多,结果越大。
LogTrick 入门教程,包含原地写法,以及额外维护一个列表的写法。
如果你不熟悉原地去重算法,可以看 26. 删除有序数组中的重复项,我的题解。
模板:
# 对于每个右端点 i,计算所有子数组的或值,打印这些或值的分布范围(子数组左端点范围)
# 时间复杂度 O(nlogU),其中 U = max(nums)
def logTrick(nums: List[int]) -> None:
or_left = [] # (子数组或值,最小左端点)
for i, x in enumerate(nums):
# 计算以 i 为右端点的子数组或值
for p in or_left:
p[0] |= x # **根据题目修改**
# x 单独一个数作为子数组
or_left.append([x, i])
# 原地去重(相同或值只保留最左边的)
idx = 1
for j in range(1, len(or_left)):
if or_left[j][0] != or_left[j - 1][0]:
or_left[idx] = or_left[j]
idx += 1
del or_left[idx:]
print(i, x)
for k, (or_val, left) in enumerate(or_left):
right = or_left[k + 1][1] - 1 if k < len(or_left) - 1 else i
# 对于左端点在 [left, right],右端点为 i 的子数组,OR 值都是 or_val
print(left, right, or_val)
logTrick([4, 2, 1, 5])注:下面的部分题目,可以用滑动窗口+栈做到更优的 时间复杂度,见 原理讲解(方法二)。
十进制拆位
模板(最大异或和):
class XorBasis:
# n 为值域最大值 U 的二进制长度,例如 U=1e9 时 n=30
def __init__(self, n: int):
self.b = [0] * n
def insert(self, x: int) -> None:
b = self.b
# 从高到低遍历,保证计算 max_xor 的时候,参与 XOR 的基的最高位(或者说二进制长度)是互不相同的
for i in range(len(b) - 1, -1, -1):
if x >> i: # 由于大于 i 的位都被我们异或成了 0,所以 x >> i 的结果只能是 0 或 1
if b[i] == 0: # x 和之前的基是线性无关的
b[i] = x # 新增一个基,最高位为 i
return
x ^= b[i] # 保证每个基的二进制长度互不相同
# 正常循环结束,此时 x=0,说明一开始的 x 可以被已有基表出,不是一个线性无关基
def max_xor(self) -> int:
b = self.b
res = 0
# 从高到低贪心:越高的位,越必须是 1
# 由于每个位的基至多一个,所以每个位只需考虑异或一个基,若能变大,则异或之
for i in range(len(b) - 1, -1, -1):
if res ^ b[i] > res: # 手写 max 更快
res ^= b[i]
return res贪心、脑筋急转弯等。
欢迎关注 B站@灵茶山艾府
如果你发现有题目可以补充进来,欢迎评论反馈。