bitset
基础操作
std::bitset 是标准库中的一个存储 0/1 的大小不可变容器
头文件:
1 | |
定义:
1 | |
基本成员函数:
| 操作写法 | 含义 | 时间复杂度 |
|---|---|---|
[] |
访问 or 修改 某一个下标的值 | \(\mathcal O(1)\) |
count() |
返回 true 的数量。 |
\(\mathcal O(\frac{N}{w})\) |
all() |
检查所有位是否为 1 |
\(\mathcal O(\frac{N}{w})\) |
any() |
检查是否存在 1 位 |
\(\mathcal O(\frac{N}{w})\) |
none() |
检查是否所有位为 0 |
\(\mathcal O(\frac{N}{w})\) |
set() |
所有位置变为 1 |
\(\mathcal O(\frac{N}{w})\) |
reset() |
所有位置变为 0 |
\(\mathcal O(\frac{N}{w})\) |
& |
按位与 | \(\mathcal O(\frac{N}{w})\) |
\| |
按位或 | \(\mathcal O(\frac{N}{w})\) |
^ |
按位异或 | \(\mathcal O(\frac{N}{w})\) |
^ |
按位取反 | \(\mathcal O(\frac{N}{w})\) |
<< & >> |
左移 & 右移 | \(\mathcal O(\frac{N}{w})\) |
bitset<N>(unsigned long long) |
把一个 64 位无符号整数转换为 bitset |
\(\mathcal O(N)\) |
(这里的操作并非所有,只是大多数如 flip() 都可以直接用位运算操作 ~ 代替,但是其中有一些操作还是很有必要的,如 count() 如果暴力实现时间复杂的远大于直接调用)
(这里的 w 取决于你电脑的性能,64 位 w 为 64,32 位 w 为 32)
这里有两种理解方式:
-
把他当作一个
bool数组 -
把他当作一个超级大的无符号整数
基本用途
输出一个整数的二进制:
直接 cout<<bitset<N>(x);,注意其中 N 一定是一个 const 类型的常量或一个整数
(这是因为 std::bitset 重载了输入,输出,同理也可以用 cin>>x,输入一个 01 字符串)
卡长:
std::bitset,时间复杂度,空间复杂度在某一些操作都会比 bool 数组小,可以用于卡长。
简化代码:
其集成了许多函数,可以在一些需要的题目中快速完成,如求解一个 x 的 二进制下 1 的个数,可以直接写成 (bitset<N>(x)).count(),时间复杂度近似 \(\mathcal O({min(N,64)})\)
扩展用途
bitset 优化 DP:
比如说对于这一道题:P6345 [CCO 2017] 接雨滴 - 洛谷。
这道题虽然是紫题,但是其实难点主要是在推 DP 转移方程。
我们先不考虑 bitset,可以写出一份代码:
1 2 3 4 5 6 7 8 | |
因为 DP 数组存储的是 bool, 我们发现如果吧把 DP 数组 bool f[p][t] 转化 bitset<T> f[p],则现在的时间复杂的会变成 \(\mathcal O(\frac {n^2m}{w})\):
1 2 3 4 5 | |
则现在可以通过此题。
其实简单来说,这样写仍然是在进行 卡长
集合相关
对于两个不可重的集合,我们将他们值当作下标存在 bitset 中,两个进行与操作即可快速得到他们的并集
可以看到 P4688 [Ynoi Easy Round 2016] 掉进兔子洞 - 洛谷
我们发现其实 ans = len1+len2+len3-3*(相同元素个数).
我们可以将每一个区间的值按下标存储到 bitset 中,然后直接进行 & 操作,即可得到其相同元素个数。
然后问题就解决了?
我们注意到按照下标存储处理的一定是 不可重集合,而这一道题要求可重,所以我们需要先对其进行离散化
如对于全集为 {1,1,2,3,3},处理区间集合为 {1,2,3,3}
存储的 bitset 为:
1 2 3 | |
这个样子才能解决。
然后问题就解决了?
与莫队结合
还是这道题:P4688 [Ynoi Easy Round 2016] 掉进兔子洞 - 洛谷
我们发现如果每一次都重新把每一个区间存入其中,与暴力没什么两样,但是我们发现每一个区间之间有很大的重叠,所以考虑与莫队结合,这样就可以以大概为 \(\mathcal O(N\sqrt{N})\) 的时间处理每一次把对应区间元素插入集合。 但是还有一点需要注意:
虽然说 bitset 的空间复杂度比较小,但是因为是离线,我们要求把每一次询问产生的 bitset 记录下来,一遍统计答案,所以 \(\mathcal O(\frac{N^2}{w})\) 会炸,需要将其分为多个部分(每一个处理 2e4 次询问)。
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | |
优化埃式筛
其实就是把埃式筛的数组替换为 bitset,这里有一份对比的代码:
对比代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |