Pack 64 booleans into a machine word and operate on all of them at once. That divides by and turns simple operations into . With AVX2 pragmas the compiler processes 256 bits per instruction.

std::bitset

bitset<N> b;                     // N must be a compile-time constant
b.set(i); b.reset(i); b.flip(i); b[i];
b.count(); b.any(); b.none();
b |= c; b &= c; b ^= c; b <<= k; b >>= k;   // O(N / 64)
for (int i = b._Find_first(); i < N; i = b._Find_next(i)) {}   // iterate set bits (GCC)

For a runtime size, either pick the next power of two from a template list (template<int L> void solve() dispatched on ), or write a vector<ull> bitset by hand. Shifts across words need a carry from the neighbouring word.

Classic speedups

ProblemBitset formCost
subset sum / 0-1 knapsack feasibilitydp |= dp << w
subset sum with bounded countsbinary splitting of multiplicities, then shifts when values (at most distinct values)

Patterns

  • Subset sum with reconstruction: keep a bitset per item (memory bytes) or use divide and conquer on items.
  • Knapsack where the answer is “which sums are possible”: bitsets beat the DP by 64x; with a count limit per value, group equal values and split into powers of two.
  • Sliding-window or dynamic bitsets: maintain one bitset per value (positions where ); “positions such that for all ” is an AND of shifted bitsets.
  • Bitset over the value domain for “does a pair with exist”: convolution would be FFT, but a bitset gives an alternative for moderate .
  • Offline queries in chunks: if bitsets of size don’t fit in memory ( gives 1.25 GB), process vertices or queries in blocks of 64 or 4096 and reuse the memory.

When it pays

The inner loop is a simple AND/OR/XOR/shift over independent boolean entries, with no data-dependent branching. Values that aren’t boolean can sometimes be split by bit or by threshold. vector<bool> is also bit-packed, but it has no bulk operations, so it doesn’t count.

Practical: #pragma GCC target("avx2") gives a further 2-4x on bitset loops. Memory is bytes per bitset. Iterating set bits with _Find_next costs , not — the win that matters for a sparse bitset.

Bitsets are the accessible form of word-level parallelism: the same “process 64 independent booleans per instruction” idea behind fusion trees (Theoretical Algorithms Catalog), Bitap/shift-or string matching, and the four-Russians technique. Whenever an inner loop does the same simple thing to many independent booleans, it can probably be done 64 at a time.