bitset
Giới thiệu
std::bitset là container kích thước cố định lưu 0/1. Nói строго, nó không thuộc STL.
bitset và 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.
——Trích từ “The C++ Standard Library 2nd Edition”
Từ đó thấy bitset không thuộc STL mà là “Special Container” trong thư viện chuẩn. Thực tế, nó cũng không thỏa yêu cầu của container STL. Nó không phải adapter và cũng không phụ thuộc container STL khác làm nền.
Do bộ nhớ được địa chỉ theo byte, không theo bit; một biến bool chỉ biểu diễn 0/1 nhưng chiếm 1 byte.
bitset tối ưu để 8 bit trong 1 byte lưu 8 giá trị 0/1.
Với biến int 4 byte, nếu chỉ lưu 0/1, bitset tốn \(\frac{1}{32}\) bộ nhớ, và thời gian xử lý cũng khoảng \(\frac 1{32}\).
Trong một số trường hợp, bitset tối ưu hiệu năng. Việc tối ưu là giảm độ phức tạp hay hằng số tùy góc nhìn. Độ phức tạp thường viết:
- \(O(n)\), coi như không tối ưu.
- \(O(\frac n{32})\), không nghiêm ngặt (không nên có hằng số), nhưng thể hiện tối ưu \(\frac 1{32}\).
- \(O(\frac n w)\), với \(w=32\), cách viết phổ biến.
- \(O(\frac n {\log w})\), với \(w\) là số bit của một biến nguyên.
Ngoài ra, vector<bool> có cách lưu giống bitset, khác ở chỗ hỗ trợ cấp phát động. Nhưng bitset có nhiều hàm tiện, đôi khi hỗ trợ SIMD giảm hằng số. vector<bool> còn có hành vi khác vector (ví dụ &vec[0] + i không bằng &vec[i]). Vì vậy thường không dùng vector<bool>.
Cách dùng
Xem std::bitset - cppreference.com.
Header
1 | |
Chỉ định kích thước
1 | |
Hàm dựng
bitset(): mọi bit làfalse.bitset(unsigned long val): đặt theo nhị phân củaval.bitset(const string& str): đặt theo chuỗi \(01\)str.
Toán tử
-
operator []: truy cập một bit. -
operator ==/operator !=: so sánh haibitset. -
operator &/operator &=/operator |/operator |=/operator ^/operator ^=/operator ~: AND/OR/XOR/NOT.Lưu ý:
bitsetchỉ có thể làm phép bit vớibitset; muốn làm với số nguyên phải chuyển sangbitset. -
operator <</operator >>/operator <<=/operator >>=: dịch trái/phải.
bitset cũng hỗ trợ I/O kiểu stream, nên có thể dùng cin/cout.
Hàm thành viên
count(): số bittrue.size(): kích thướcbitset.test(pos): giốngat()củavector, khác[]ở chỗ kiểm tra biên.any(): có bittruenào không.none(): tất cả đềufalse.all(): tất cả đềutrue.-
set(): đặt toàn bộtrue.set(pos, val = true): đặt một bittrue/false.
-
reset(): đặt toàn bộfalse.reset(pos): đặt một bitfalse(tương đươngset(pos, false)).
-
flip(): đảo tất cả bit (\(0\leftrightarrow1\)).flip(pos): đảo một bit.
to_string(): trả về chuỗi.to_ulong(): trả vềunsigned long(longtrên NT/32-bit POSIX nhưint, trên 64-bit POSIX nhưlong long).to_ullong(): (từ C++11) trả vềunsigned long long.
Ngoài ra, libstdc++ có một số hàm nội bộ1:
_Find_first(): trả về vị trítrueđầu tiên, nếu không có trả về kích thước._Find_next(pos): trả về vị trítrueđầu tiên saupos(chỉ số >pos), nếu không có trả về kích thước.
Ứng dụng
“LibreOJ β Round #2”贪心只能过样例
Bài này có thể dùng dp, chuyển trạng thái:
\(f(i,j)\) là với \(i\) số đầu, tổng bình phương có thể bằng \(j\) hay không, thì \(f(i,j)=\bigvee\limits_{k=a}^bf(i-1,j-k^2)\).
Nếu làm trực tiếp là \(O(n^5)\), (trông có vẻ) không qua được.
Dùng bitset để tối ưu, dịch trái và OR là được:
Bản nộp: 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 | |
Vì libstdc++ nén theo __CHAR_BIT__ * sizeof(unsigned long)2, trên vài nền tảng là \(32\). Có thể tự viết bitset (chỉ cần hỗ trợ dịch trái rồi OR) nén \(64\) bit (__CHAR_BIT__ * sizeof(unsigned long long)) để tối ưu thêm:
Bản nộp: bitset tự viết
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 | |
Ngoài ra, vét cạn kèm vài cắt tỉa cũng qua được:
Bản nộp: Vét cạn có cắt tỉa
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
Đề bài
Cho \(n\) đa tập, 4 thao tác:
- Gán một đa tập bằng một số.
- Gán một đa tập bằng tổng của hai đa tập khác.
- Gán một đa tập bằng \(\gcd\) của mỗi cặp phần tử lấy từ hai đa tập khác: \(A=\{\gcd(x,y)|x\in B,y\in C\}\).
- Hỏi số lần xuất hiện của một số trong một đa tập, trong mô-đun 2.
Số đa tập \(10^5\), số thao tác \(10^6\), giá trị tối đa \(7000\).
Cách làm
Thấy “mod 2” có thể nghĩ đến bitset để lưu mỗi đa tập.
Khi đó, thao tác 1 là gán trực tiếp, thao tác 2 là XOR (vì mod 2), thao tác 4 là truy vấn. Nhưng thao tác 3 thì sao?
Có thể lưu đa tập các ước của mỗi đa tập. Khi đó thao tác 3 là AND.
Ta tiền xử lý bitset các ước của mỗi số trong phạm vi, thao tác 1 giải quyết được, thao tác 2 vẫn là XOR.
Vấn đề là từ đa tập ước \(A'\) suy ra số lần xuất hiện của \(x\) trong đa tập \(A\).
Gọi đa tập gốc là \(A\), đa tập ước là \(A'\). Cần số lần \(x\) xuất hiện trong \(A\), dùng Möbius inversion:
Vì là mod 2, \(-1\) và \(1\) như nhau, chỉ cần xem \(\frac d x\) có thừa số bình phương không. Do đó, tiền xử lý cho mỗi \(x\) một bitset gồm các bội của \(x\) mà chia cho \(x\) không có thừa số bình phương. Khi hỏi, AND rồi count().
Độ phức tạp mỗi truy vấn \(O(\frac v w)\) (\(v=7000,\,w=32\)).
Tiền xử lý có thể \(O(v\sqrt v)\) hoặc \(O(v^2)\) cho đơn giản; cách \(\log\) như dưới có độ phức tạp cấp điều hòa nên là \(O(v\log v)\).
Mã tham khảo
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 | |
Kết hợp với sàng Eratosthenes
Do bitset có khả năng đọc/ghi liên tiếp rất nhanh, nó rất phù hợp để kết hợp với sàng Eratosthenes.
Cách dùng đơn giản: thay mảng bool trong sàng bằng bitset.
Kiểm thử tốc độ
Dùng Quick C++ Benchmarks, compiler GCC 13.2, tham số -std=c++20 -O2.
| Thuật toán | Tên hàm |
|---|---|
| Sàng Eratosthenes + mảng bool C, không lưu prime | Eratosthenes_CArray |
Sàng Eratosthenes +vector<bool>, không lưu prime |
Eratosthenes_vector |
Sàng Eratosthenes +bitset, không lưu prime |
Eratosthenes_bitset |
| Sàng Eratosthenes + mảng bool C, lưu prime | Eratosthenes_CArray_sp |
Sàng Eratosthenes +vector<bool>, lưu prime |
Eratosthenes_vector_sp |
Sàng Eratosthenes +bitset, lưu prime |
Eratosthenes_bitset_sp |
| Sàng Euler + mảng bool C | Euler_CArray |
Sàng Euler +vector<bool> |
Euler_vector |
Sàng Euler +bitset |
Euler_bitset |
-
Khi sàng Eratosthenes lưu prime:
-
Khi sàng Eratosthenes không lưu prime:
Từ kết quả:
- Sàng Eratosthenes \(O(n \log \log n)\) khi tối ưu bằng
bitsethoặcvector<bool>còn nhanh hơn sàng Euler \(O(n)\); - Sàng Euler dùng
bitsethoặcvector<bool>thường không cải thiện nhiều; bitsetnhỉnh hơnvector<bool>.
Mã tham khảo
Cần cài google/benchmark.
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 | |
Kết hợp với phân khối trên cây
bitset kết hợp phân khối trên cây giải được các bài toán tổng hợp thông tin trên nhiều đường đi; xem Cấu trúc dữ liệu/Phân khối trên cây.
Kết hợp với Mo's algorithm
Xem Khác/莫队配合 bitset.
Tính thứ tự từng chiều cao
Xem slide FHR.
Tài liệu tham khảo và chú thích
Last updated on this page:, Update history
Found an error? Want to help improve? Edit this page on GitHub!
Contributors to this page:i-Yirannn, Xeonacid, ouuan
All content on this page is provided under the terms of the CC BY-SA 4.0 and SATA license, additional terms may apply



