【学习笔记】bitset
简介
bitset 是一种高效储存和运算二进制的容器。
时间复杂度与空间复杂度都为 \(O(\frac{n}{w})\),其中 \(w\) 约为 \(32\)。
bitset 的声明
\\声明一个大小为 100010 的 bitset
bitset<100010>bs
\\声明一个大小为 5 的 bitset,并将其初始化为 101010(42 的二进制)
\\但是由于 42 的长度大于 5 所以高位会被自动截断
bitset<5>bs(42)
bitset 可以像数组一样用 [] 进行访问,也可以像数组一样更改值,注意 bitset 最低为为 \(0\),且是从右边开始。
bitset 成员函数
单次使用 \(O(1)\) 的成员函数
set(pos)将第 pos 位设为 \(1\)。reset(pos)将第 pos 位设为 \(0\)。flip(pos)翻转第 pos 位。
时间复杂度 \(O(\frac{n}{w})\) 的成员函数
set()将所有位都设置为 1。reset()将所有位都设置为 0。flip()翻转所有位。count()返回值为 1 的位的数量。size()返回 bitset 的总大小。any()检查是否有任何一位是 1。none()检查是否所有位都是 0。all()检查是否所有位都是 1。
位运算(时间复杂度 \(O(\frac{n}{w})\))
bitset 重载了常见的位运算符,可以方便地在两个 大小相同 的 bitset 之间进行运算。
&(与)|(或)^(异或)~(非)<<(左移)>>(右移)
例题cf878d
考虑只有 \(0,1\) 的情况:显然可以对 \(k\) 种生物,每种生物 \(i\) 都开一个大小为 \(n\) 的 bitset \(b_i\)。\(\max(x,y)\) 操作就是 \(b_x | b_y\),\(\min(x,y)\) 操作就是 \(b_x \& b_y\)。
拓展到一般情况:可以将 \(n\) 列的 \(k\) 个数字离散化到 \(1\sim k\) 的值域当中。假设 \(a_{i,j}=v\),可以将它变成 \(2^v - 1\),存到 bitset 中,这样原来的 \(nk\) 个数就以 \(0,1\) 的形式存到了 \(nk^2\) 大小的 bitset 中(每个数占用 \(k\) 大小的 bitset,有 \(nk\) 个数),这样就可以继续用 bitset 维护。时间复杂度 \(O(nkq/\omega+nk^2)\)。
考虑优化:发现维护了很多冗余的信息,因为最多只有 \(2^k\) 个本质不同的列,但是现在实际上维护了 \(nk\) 个列。考虑两个相同的列,不管怎么操作它们都还是相同的,因此不管如何操作,最后本质不同的列还是只有 \(2^k\)。
可以先预处理出 \(k\times 2^k\) 大小的 bitset,每次操作就把两个大小为 \(2^k\) 的 bitset 进行位运算,将新生成的一个 bitset 放到下面。设 \(id_{i,j}\) 表示第 \(i\) 大列,第 \(j\) 小列的类型(共 \(n\) 大列,每一大列有 \(k\) 小列),如果要查询 \(a_{i,j}\) 的值,可以先算出出 \(\sum_{s = 1}^k b_{i,id_{j,s}}\) 即可,即是第几大的数,然后在映射回离散化前的数值。时间复杂度 \(O(2 ^ kq/\omega + k/\omega)\)

浙公网安备 33010602011771号