8.1 The Six Core Operators
Suppose we have two 8-bit numbers:
A = 0011_1100 (60)
B = 0000_1101 (13)
| Operator | Name | Rule | Example (A, B) |
Result |
|---|---|---|---|---|
& |
AND | 1 only if both bits are 1 | A & B |
0000 1100 (12) |
| |
OR | 1 if at least one bit is 1 | `A | B` |
^ |
XOR | 1 if bits differ | A ^ B |
0011 0001 (49) |
~ |
NOT | flips every bit | ~A |
1100 0011 (-61) |
<< |
Left shift | Shift all bits left, fill with 0 | A << 2 |
1111 0000 (240) |
>> |
Right shift | Shift all bits right | A >> 2 |
0000 1111 (15) |
Useful Shift Tricks
Competitive programmers rarely write pow(2, n) because floating-point operations are slow and prone to precision issues. Instead:
2^n→1 << nx * 2→x << 1x / 2→x >> 1(common in binary search:mid = (l + r) >> 1)
8.2 Handy Bitwise Hacks
-
Check parity:
if (x & 1)→xis odd.if (!(x & 1))→xis even. Odd numbers have the least significant bit set to 1.
-
Swap without temp (for fun, not recommended in production):
a ^= b; b ^= a; a ^= b; -
Find the lone number: An array has numbers appearing twice except one unique element. Trick: XOR all numbers. Because
a ^ b ^ a = b, the duplicates cancel out and you are left with the odd-one-out.
8.3 Bitmasking: Encode a Set in an Integer
This is the strongest application of bit tricks. Suppose you have N switches (N ≤ 20). Each can be on or off. Instead of an array of bools, use a single integer state.
If state = 21 (binary 10101):
- Bit 0 = 1 → switch 0 is on.
- Bit 1 = 0 → switch 1 is off.
- Bit 2 = 1 → switch 2 is on.
- …
Core operations (0-indexed):
- Check:
if (state & (1 << i)) - Add:
state = state | (1 << i); - Remove:
state = state & ~(1 << i);~(1 << i)creates a mask with all bits 1 except biti. - Toggle:
state = state ^ (1 << i);
8.4 Lowbit: The Foundational Trick Behind Fenwick Trees
lowbit(x) returns the value of the lowest set bit in x.
x = 12 (1100)→lowbit(12) = 4 (100)x = 6 (0110)→lowbit(6) = 2 (10)x = 7 (0111)→lowbit(7) = 1 (1)
int lowbit(int x) {
return x & (-x);
}Reason: In two’s complement, -x equals ~x + 1. The AND operation strips all bits except the lowest 1.
Example application: Count the number of 1-bits in x by repeatedly subtracting lowbit(x).
int countSetBits(int x) {
int cnt = 0;
while (x > 0) {
x -= x & -x;
cnt++;
}
return cnt;
}
// C++20 offers std::popcount(x)8.5 GCC Built-ins (GCC/G++ Specific)
If you use GCC (Luogu, Codeforces), there are blazing-fast built-ins that save you from writing loops:
__builtin_popcount(x): number of 1s inx.__builtin_ctz(x): count trailing zeros (number of zeros at the end).__builtin_clz(x): count leading zeros (works for 32-bit ints).
⚠️ For
long long, appendll: e.g.,__builtin_popcountll(x).
🧠 Chapter Summary & Practice
Bit tricks might look mystical, but they are simply the right tools for representing sets and states compactly.
Practice problems:
P1469 Find Chopsticks(Luogu): find the lone number when every other number appears twice—O(N) and minimal extra space (hint: XOR).LeetCode 78. Subsets: generate all subsets using bitmasking (0to2^N - 1).P1226 Fast Power: revisit the previous chapter’s template and observe howb & 1andb >>= 1are bit tricks.
With bit operations in your belt, you now have the mindset for prefix sums and difference arrays (they are all about incremental updates). Next chapter introduces your first “preprocessing” technique: how to answer any range sum in O(1) time.