bitset
Introduction
std::bitset is a fixed-size container in the standard library that stores 0/1 values. Strictly speaking, it is not part of the STL.
bitset and STL
The C++ standard library provides some special container classes, the so-called container adapters (stack, queue, priority queue). In addition, a few classes provide a container-like interface (for example, strings, bitsets, and valarrays). All these classes are covered separately.1 Container adapters and bitsets are covered in Chapter 12.
The C++ standard library provides not only the containers for the STL framework but also some containers that fit some special needs and provide simple, almost self-explanatory, interfaces. You can group these containers into either the so-called container adapters, which adapt standard STL containers to fit special needs, or a bitset, which is a containers for bits or Boolean values. There are three standard container adapters: stacks, queues, and priority queues. In priority queues, the elements are sorted automatically according to a sorting criterion. Thus, the "next" element of a priority queue is the element with the "highest" value. A bitset is a bitfield with an arbitrary but fixed number of bits. Note that the C++ standard library also provides a special container with a variable size for Boolean values: vector.
— Excerpt from The C++ Standard Library 2nd Edition
Thus, bitset is not part of the STL but is a "Special Container" in the standard library. In fact, as a container, it does not meet the requirements of STL containers. Calling it an adapter is also inaccurate, as it does not rely on other STL containers for its underlying implementation.
Since memory addresses are byte-addressable rather than bit-addressable, a bool variable, although it can only represent 0/1, occupies 1 byte of memory.
bitset optimizes storage by packing eight bits into a single byte, allowing each bit to store a 0/1 value.
For a 4-byte int variable, when storing only 0/1 values, bitset uses only \(\frac{1}{32}\) of its space. When computing certain information, the time required is also \(\frac{1}{32}\) of the original.
In some cases, bitset can optimize program efficiency. Whether it optimizes complexity or constants depends on the perspective. Generally, the complexity of bitset can be expressed in the following ways (assuming the original complexity is \(O(n)\)):
- \(O(n)\), which considers that
bitsetdoes not optimize complexity at all. - \(O(\frac n{32})\), which is not rigorous (constants should not appear in complexity) but reflects that
bitsetcan reduce the required time to \(\frac{1}{32}\). - \(O(\frac n w)\), where \(w=32\) (the word size of the computer). This notation is widely accepted.
- \(O(\frac n {\log w})\), where \(w\) is the size of an integer variable on the computer.
Additionally, a specialization of vector, vector<bool>, stores data in the same way as bitset. The difference is that vector<bool> supports dynamic resizing, while bitset is statically allocated at compile time, similar to a regular static array. However, bitset provides useful library functions that are not only convenient but can also enable SIMD optimizations to reduce constants. Moreover, vector<bool> behaves inconsistently with vector in some aspects (e.g., for std::vector<bool> vec, &vec[0] + i is not equal to &vec[i]). Therefore, vector<bool> is generally not used.
Usage
Refer to std::bitset - cppreference.com.
Header File
1 | |
Specifying Size
1 | |
Constructors
bitset(): All bits arefalse.bitset(unsigned long val): Set to the binary representation ofval.bitset(const string& str): Set to the binary stringstr.
Operators
-
operator []: Access a specific bit. -
operator ==/operator !=: Compare whether twobitsets are identical. -
operator &/operator &=/operator |/operator |=/operator ^/operator ^=/operator ~: Perform bitwise AND/OR/XOR/NOT operations.Note:
bitsetcan only perform bitwise operations with anotherbitset. To perform bitwise operations with an integer, the integer must first be converted to abitset. -
operator <</operator >>/operator <<=/operator >>=: Perform binary left/right shifts.
Additionally, bitset supports C++ stream I/O, meaning you can input/output via cin/cout.
Member Functions
count(): Returns the number oftruebits.size(): Returns the size of thebitset.test(pos): Similar toat()invector, performs bounds checking, unlike[].any(): Returnstrueif any bit istrue, otherwisefalse.none(): Returnstrueif all bits arefalse, otherwisefalse.all(): Returnstrueif all bits aretrue, otherwisefalse.-
set(): Sets all bits totrue.set(pos, val = true): Sets a specific bit totrue/false.
-
reset(): Sets all bits tofalse.reset(pos): Sets a specific bit tofalse. Equivalent toset(pos, false).
-
flip(): Flips all bits (\(0\leftrightarrow1\), equivalent to XOR with an all-onesbitset).flip(pos): Flips a specific bit.
to_string(): Returns a string representation.to_ulong(): Returns anunsigned longrepresentation (longis the same asinton NT and 32-bit POSIX systems, and the same aslong longon 64-bit POSIX).to_ullong(): (Since C++11) Returns anunsigned long longrepresentation.
Additionally, libstdc++ provides some practical internal member functions1:
_Find_first(): Returns the index of the firsttruebit, or the size of thebitsetif notruebit exists._Find_next(pos): Returns the index of the firsttruebit afterpos(strictly greater thanpos), or the size of thebitsetif no such bit exists.
Applications
「LibreOJ β Round #2」Greedy Can Only Pass Sample Cases
This problem can be solved with DP. The transition is simple:
Let \(f(i,j)\) denote whether the sum of squares of the first \(i\) numbers can be \(j\). Then \(f(i,j)=\bigvee\limits_{k=a}^bf(i-1,j-k^2)\) (logical OR).
However, a naive implementation would be \(O(n^5)\), which seems too slow.
We can optimize using bitset by shifting left and ORing:
Submission Record: std::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 | |
Since libstdc++'s implementation packs __CHAR_BIT__ * sizeof(unsigned long) bits2, which is \(32\) on some platforms, we can manually implement bitset (only needing to support left shift and OR) packing \(64\) bits (__CHAR_BIT__ * sizeof(unsigned long long)) for further optimization:
Submission Record: Handwritten 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 45 46 47 48 49 | |
Additionally, a brute-force approach with some pruning can also pass:
Submission Record: Brute-force with pruning
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 | |
CF1097F Alex and a TV Show
Problem
You are given \(n\) multisets and four types of operations:
- Set a multiset to a single number.
- Set a multiset to the sum of two other multisets.
- Set a multiset to the set of \(\gcd\) of pairs from two other multisets. That is: \(A=\{\gcd(x,y)|x\in B,y\in C\}\).
- Query the count of a number in a multiset, modulo 2.
Number of multisets: \(10^5\), number of operations: \(10^6\), value range: \(7000\).
Solution
Seeing "modulo 2", we can think of using bitset to maintain each multiset.
Thus, operation 1 is direct assignment, operation 2 is XOR (since modulo 2), operation 4 is direct lookup, but .. what about operation 3?
We can try to maintain the multiset of all divisors of each multiset. Then operation 3 becomes a direct bitwise AND.
We can precompute the bitset of divisors for each number in the value range, solving operation 1. Operation 2 remains XOR.
Now the problem is: given the multiset of divisors of a multiset, how to get the count of a specific number in the original multiset.
Let the original multiset be \(A\), and its divisor multiset be \(A'\). We want the count of \(x\) in \(A\). Using Möbius inversion:
Since we are working modulo \(2\), \(-1\) and \(1\) are the same; we only need to check whether \(\frac d x\) has any square factors. Thus, we can precompute for each number in the value range a bitset of positions among its multiples that, when divided by it, are square-free. When answering queries, we bitwise AND and then count().
The complexity per query is \(O(\frac v w)\) (\(v=7000,\,w=32\)).
As for preprocessing, \(O(v\sqrt v)\) or \(O(v^2)\) preprocessing is straightforward. Logarithmic preprocessing is shown in the code below, with complexity being the harmonic series, i.e., \(O(v\log v)\).
Reference Code
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 | |
Combined with the Sieve of Eratosthenes
Due to bitset's fast sequential read/write efficiency, it is well-suited for generating prime tables when combined with the Sieve of Eratosthenes.
The method is simple: just replace the boolean array in the Sieve of Eratosthenes with a bitset.
Speed Test
Using Quick C++ Benchmarks for testing, compiler: GCC 13.2, compile flags: -std=c++20 -O2.
| Algorithm | Function Name |
|---|---|
| Sieve of Eratosthenes + C-style bool array, no prime storage | Eratosthenes_CArray |
Sieve of Eratosthenes + vector<bool>, no prime storage |
Eratosthenes_vector |
Sieve of Eratosthenes + bitset, no prime storage |
Eratosthenes_bitset |
| Sieve of Eratosthenes + C-style bool array, with prime storage | Eratosthenes_CArray_sp |
Sieve of Eratosthenes + vector<bool>, with prime storage |
Eratosthenes_vector_sp |
Sieve of Eratosthenes + bitset, with prime storage |
Eratosthenes_bitset_sp |
| Euler's Sieve + C-style bool array | Euler_CArray |
Euler's Sieve + vector<bool> |
Euler_vector |
Euler's Sieve + bitset |
Euler_bitset |
-
When the Sieve of Eratosthenes stores the primes:
-
Test result for \(N=5 \times 10^7 + 1\):

-
Test result for \(N=10^8 + 1\):

-
-
When the Sieve of Eratosthenes does not store the primes:
-
Test result for \(N=5 \times 10^7 + 1\):

-
Test result for \(N=10^8 + 1\):

-
From the test results:
- The Sieve of Eratosthenes with complexity \(O(n \log \log n)\), when optimized with
bitsetorvector<bool>, can outperform Euler's Sieve with complexity \(O(n)\); - Euler's Sieve shows insignificant optimization with
bitsetorvector<bool>in most cases; bitsetoptimization is slightly stronger thanvector<bool>.
Reference Code
Requires google/benchmark installed.
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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | |
Combined with Tree Decomposition
bitset combined with tree decomposition can solve a class of problems involving queries on multiple tree paths. See Data Structures/Tree Decomposition.
Combined with Mo's Algorithm
See Miscellaneous/Mo's Algorithm with bitset.
Computing High-Dimensional Partial Orders
See FHR Slides.
References and Notes
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用