当前位置: 首页 > news >正文

bitset

std::bitset 是 C++ 标准库中的一个类模板,用于高效地处理固定大小的位序列。它就像一个“位”的数组,但提供了比普通数组更丰富的操作接口,并且在空间上进行了优化。

当需要处理一组布尔标志、进行位掩码操作或实现某些对位操作要求较高的算法时,bitset 是一个非常强大和方便的工具。

要使用 bitset,首先需要包含相应的头文件:

#include <bitset>

bitset 的声明需要在一个尖括号内指定其大小(即位的数量),这个大小必须是一个编译时常量。

// 声明一个大小为 32 的 bitset,所有位都被初始化为 0
std::bitset<32> b1; // b1: 00000000000000000000000000000000// 使用一个无符号整数来初始化,整数的二进制表示会被用来设置 bitset 的位
// 注意:如果整数的二进制位数超过 bitset 的大小,高位会被截断
std::bitset<8> b2(42); // 42 的二进制是 101010,所以 b2 是 00101010// 使用字符串来初始化
// 可以是 '0' 和 '1' 组成的字符串
std::bitset<8> b3(std::string("1101")); // b3: 00001101// 也可以只使用字符串的一部分
// 从索引 2 开始,取 4 个字符
std::bitset<8> b4(std::string("10110101"), 2, 4); // "1101" -> b4: 00001101

[]:像数组一样访问特定位,可以读取也可以修改。注意: bitset 的索引是从右到左的,第 0 位在最右边。

std::bitset<8> b("10101010");
std::cout << b[0]; // 输出 0 (最右边的位)
std::cout << b[1]; // 输出 1b[0] = 1; // 将第 0 位设置为 1
std::cout << b; // 输出 10101011if (b[7]) { // 检查第 7 位 (最左边的位)std::cout << "Position 7 is 1" << std::endl;
}

bitset 提供了一些修改位的成员函数。

  • set():将所有位都设置为 1。
  • set(pos):将第 pos 位设置为 1。
  • reset():将所有位都设置为 0。
  • reset(pos):将第 pos 位设置为 0。
  • flip():翻转所有位(0 变 1,1 变 0)。
  • flip(pos):翻转第 pos 位。
std::bitset<4> b; // 0000b.set(1);    // 0010
b.set(3);    // 1010
std::cout << b << std::endl; // 输出 1010b.reset(1);  // 1000
std::cout << b << std::endl; // 输出 1000b.flip();    // 0111
std::cout << b << std::endl; // 输出 0111

bitset 提供了一些查询状态的成员函数。

  • count():返回值为 1 的位的数量。
  • size():返回 bitset 的总大小。
  • any():检查是否有任何一位是 1。
  • none():检查是否所有位都是 0。
  • all():检查是否所有位都是 1。
std::bitset<8> b("00010010");
std::cout << "Count of 1s: " << b.count() << std::endl; // 输出 2
std::cout << "Size: " << b.size() << std::endl;         // 输出 8if (b.any()) {std::cout << "Contains at least one 1." << std::endl;
}
if (b.none()) {// 不会执行
}std::bitset<4> all_ones("1111");
if (all_ones.all()) {std::cout << "All bits are 1." << std::endl;
}

bitset 重载了常见的位运算符,可以方便地在两个大小相同的 bitset 之间进行运算。

  • & (与)
  • | (或)
  • ^ (异或)
  • ~ (非)
  • << (左移)
  • >> (右移)
std::bitset<4> b1("1010");
std::bitset<4> b2("0110");std::cout << "b1 & b2: " << (b1 & b2) << std::endl; // 0010
std::cout << "b1 | b2: " << (b1 | b2) << std::endl; // 1110
std::cout << "b1 ^ b2: " << (b1 ^ b2) << std::endl; // 1100
std::cout << "~b1: "     << (~b1)     << std::endl; // 0101
std::cout << "b1 << 1: " << (b1 << 1) << std::endl; // 0100 (左移一位,右边补0)
std::cout << "b1 >> 1: " << (b1 >> 1) << std::endl; // 0101 (右移一位,左边补0)

std::bitset 的高效源于其巧妙的内部实现。它并不是直接操作单个位,而是将位序列打包存储在一个 unsigned long (或 unsigned long long) 类型的数组中。

例如,一个 std::bitset<100> 在 64 位系统上,内部可能是一个包含 2 个 unsigned long long 的数组。

  • 第一个 unsigned long long 存储第 0 到 63 位。
  • 第二个 unsigned long long 存储第 64 到 99 位。

当执行 b.set(70) 这样的操作时,bitset 内部会:

  1. 定位数组索引:计算第 70 位应该在哪个整数上。70 / 64 = 1,所以它在数组的第 1 个元素上。
  2. 定位位索引:计算在该整数中的具体位置。70 % 64 = 6
  3. 执行位操作:通过位掩码和位运算(如 internal_array[1] |= (1ULL << 6);)来设置该位。

这种方式使得 bitset 能充分利用 CPU 对整数类型进行并行位操作的指令,从而获得极高的性能。

bitset 的绝大多数操作都非常快。其时间复杂度通常表示为 O(N/w),其中 Nbitset 的大小,w 是机器的字长(通常是 32 或 64)。这是因为很多操作可以一次性处理 w 个位。

  • O(1) 操作

    • 访问和修改单个位:[], set(pos), reset(pos), flip(pos)。这些操作只需要进行几次整数和位运算即可定位,是常数时间复杂度。
  • O(N/w) 操作

    • 整体操作:set(), reset(), flip()
    • 位运算:&, |, ^, ~
    • 查询:count(), any(), none(), all()
      这些操作需要遍历内部的整个整数数组,对每个整数执行一次操作,因此其速度与 N/w 成正比。对于 count(),现代 CPU 通常有 popcount 这样的专用指令,可以极快地计算一个整数中 1 的数量,进一步提升了性能。

例题:B3695 集合运算 3

本题要求对集合进行多种操作,包括修改集合元素和查询集合关系。如果直接用数组维护集合元素,对于修改操作(加/减一个值),需要遍历整个集合,时间复杂度为 \(O(|s_x|)\)。对于查询操作,也需要 \(O(|s_x| + |s_y|)\) 的时间。在 \(q\) 次操作下,这种方法会超时。

注意到集合元素的范围 \([1, m]\) 并不大(\(m \le 30000\)),可以考虑使用一种与值域相关的数据结构。std::bitset 是一个理想的选择。可以用 bitset 来表示一个集合,如果整数 \(k\) 在集合中,则 bitset 的第 \(k\) 位为 1,否则为 0。

使用 bitset 后,各种集合操作可以高效地通过位运算实现:

  • 交集 (\(A \cap B\)):对应两个 bitset按位与 (&) 操作。
  • 并集 (\(A \cup B\)):对应两个 bitset按位或 (|) 操作。
  • 对称差 (\(A \Delta B\)):对应两个 bitset按位异或 (^) 操作。
  • 元素全体加 \(y\):相当于 bitset 向左移位 (<<) \(y\) 位。
  • 元素全体减 \(y\):相当于 bitset 向右移位 (>>) \(y\) 位。

std::bitset 的位运算和移位操作都非常快,其复杂度与 bitset 的大小和机器字长有关,通常为 \(O(m/w)\),其中 \(w\) 是机器字长(如 64)。

参考代码
#include <cstdio>
#include <bitset>
using namespace std;
const int N = 30005; // 根据 m 的最大值 30000 设定
bitset<N> bs[N], full, tmp; // bs[i] 存储集合 s_i, full 用于操作后保留 [1, m] 范围内的数, tmp 是临时变量
int main()
{int n, m, q;scanf("%d%d%d", &n, &m, &q);// 初始化 full bitset,将 1 到 m 的位置为 1,用于限制元素范围for (int i = 1; i <= m; i++) full[i] = 1;// 读入 n 个集合for (int i = 1; i <= n; i++) {int c; scanf("%d", &c);while (c--) {int s; scanf("%d", &s);bs[i][s] = 1; // 将集合中的元素 s 在 bitset 的对应位置为 1}}// 处理 q 次操作while (q--) {int o, x, y; scanf("%d%d%d", &o, &x, &y);if (o == 1) { // 操作 1: 集合 sx 中所有元素加上 ybs[x] <<= y; // 左移 y 位,相当于所有元素值加上 ybs[x] &= full; // 与 full 按位与,去掉所有 > m 的元素} else if (o == 2) { // 操作 2: 集合 sx 中所有元素减去 ybs[x] >>= y; // 右移 y 位,相当于所有元素值减去 ybs[x] &= full; // 去掉可能由高位移入的 1 (虽然 bitset 移入的是 0,但这是一个好习惯)} else if (o == 3) { // 操作 3: 查询交集大小tmp = bs[x] & bs[y]; // 交集对应按位与printf("%d\n", (int)tmp.count()); // .count() 返回 bitset 中 1 的个数} else if (o == 4) { // 操作 4: 查询并集大小tmp = bs[x] | bs[y]; // 并集对应按位或printf("%d\n", (int)tmp.count());} else { // 操作 5: 查询对称差大小tmp = bs[x] ^ bs[y]; // 对称差对应按位异或printf("%d\n", (int)tmp.count());}}return 0;
}
http://www.hskmm.com/?act=detail&tid=21562

相关文章:

  • 网易雷火胡志鹏:AI驱动未来,游戏科技重塑虚拟创造力与现实生产力
  • idea打包推送maven仓库及同时推送到不同的maven仓库,本地和云上的腾讯云
  • 2025 阳台柜厂家最新推荐榜:企业资质 / 材质 / 服务全景解析,选对品牌少走弯路杭州阳台柜/浙江阳台柜厂家推荐
  • 2025 年除湿机厂家最新权威推荐排行榜:实力厂家技术口碑评测及场景适配选购指南吊顶/泳池/车库/防爆/调温/新风除湿机厂家推荐
  • 2025 年液氨蒸发器厂家联系方式,众众电热:多领域加热设备供应与定制化解决方案提供商
  • 【Batch】批量修改文件后缀
  • 【solace】基于docker部署solace环境
  • 2025 年阿里巴巴开通实力商家店铺开通代运营 / 阿里巴巴新手开店全托管代运营公司推荐:南鑫粤网络 —— 全域运营解决方案与实战服务优势解析
  • Vue-element-admin开发指南 - 教程
  • 2025 年国内工作服厂家最新推荐排行榜:聚焦工艺设计与服务,精选权威榜单助企业采购冬季/春季/工人/车间/防静电/餐饮/劳保工作服厂家推荐
  • ClickHouse 窗口函数使用详解(一) - 若
  • ClickHouse 窗口函数详解:告别 GROUP BY 的局限性,实现灵活数据分析 - 若
  • 简单WEB网站
  • AtCoder AGC044 总结
  • UOJ#32【UR #2】跳蚤公路 题解
  • 2025 年窗帘杆源头厂家最新推荐榜单:包含支架 / 环 / 全自动 / 可伸缩等多类产品及配件,帮助选到品质与交期双优的优质厂家
  • 2025 年电动窗帘厂家推荐榜单:聚焦国内优质企业定制实力与口碑,为采购者提供最新选择参考电动窗帘系统/电机/轨道/配件/智能电动窗帘厂家推荐
  • Vue3 使用注意事项
  • ClickHouse ReplacingMergeTree 去重陷阱:为什么你的 FINAL 查询无效? - 若
  • js中?? 和 || 的区别详解
  • 微信机器人API接口| 个人开发者必备
  • 直击现场! “ 直通乌镇 ”开源赛复赛收官,OpenCSG担任评委,十强藏着哪些产业机会?
  • Python 列表生成式、字典生成式与生成器表达式
  • java 解析json字符串,获取特定的字段值,JsonObject
  • python 批量提取txt数据中的值写入csv
  • 回忆中学的函数
  • Java 一行一行的读取文本,小Demo 大学问
  • 家里wifi电信出口ip如何控制不变,解决访问云服务器上面的资源
  • 2025 年挤压造粒机源头厂家最新推荐榜单:前五企业技术实力、服务能力及口碑测评指南对辊挤压/化肥挤压/干粉挤压造粒机厂家推荐
  • MYSQL数据库取消表的约束