0% found this document useful (0 votes)
4 views25 pages

Bit Manipulation Notes

The document provides comprehensive notes on bit manipulation, covering topics such as decimal to binary conversion, bitwise operators, and tricks for efficient operations. It emphasizes the speed advantages of bit manipulation over traditional methods, detailing algorithms and code snippets for various operations. Key insights include the use of complements for negative numbers and the significance of bitwise operations in optimizing performance.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views25 pages

Bit Manipulation Notes

The document provides comprehensive notes on bit manipulation, covering topics such as decimal to binary conversion, bitwise operators, and tricks for efficient operations. It emphasizes the speed advantages of bit manipulation over traditional methods, detailing algorithms and code snippets for various operations. Key insights include the use of complements for negative numbers and the significance of bitwise operations in optimizing performance.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

MANIPULATION BIT

Complete Study Notes • Binary • Operators • Tricks • Problems


Brute Force → Optimal Approach | Time & Space Complexity | Code Snippets

01 Decimal ↔ Binary 02 1s & 2s Complement 03 Bitwise Operators

04 Shift Operators 05 Negative Numbers 06 Bit Tricks

07 Check / Set / Clear Bit 08 Toggle / Remove Set Bit 09 Power of 2 Check

10 Count Set Bits 11 Swap Two Numbers 12 Single Number I/II/III

13 Power Set (Subsets) 14 XOR in Range 15 Divide without */ ÷

16 Min Bit Flips to Goal 17 Prime Factors & Sieve 18 Power Exponentiation

■ Key Insight: Bit manipulation is generally FASTER than generic methods — O(1) operations
instead of O(log n) or O(n)

Bit Manipulation — Complete Study Notes Page 1


Decimal ↔ Binary Conversion
01
Why does this matter?

Computers only understand 0 and 1 — every integer is stored in binary. Understanding conversions is
the foundation of ALL bit manipulation.

DECIMAL → BINARY
Algorithm: Keep dividing by 2 until the number becomes 1.
At each step, record the remainder (0 or 1).
When the number reaches 1, read remainders from bottom to top — that is your binary.

Step n n÷2 Remainder

1 7 3 1 ← (read last)

2 3 1 1

3 (stop) 1 — 1 ← (read first)

(7)■■ → (111)■
Read remainders bottom-to-top: 1, 1, 1

Another example — (13)■■ → (1101)■:


13 ÷ 2 = 6 R1 | 6 ÷ 2 = 3 R0 | 3 ÷ 2 = 1 R1 | quotient=1
Read remainders bottom-to-top: 1, 1, 0, 1 → 1101

Code: Decimal → Binary (String)


o o o Code

string convert2Binary(int n) {
string res = "";
while (n != 1) {
if (n % 2 == 1) res += "1";
else res += "0";
n = n / 2;
}
reverse(res); // We collected from LSB, reverse to get MSB first
return res;
}

■ T: O(log■ n) | S: O(log■ n) — there are log■ n remainders to store

BINARY → DECIMAL
Algorithm: Start from the Right-Most Bit (RMB), index = 0. Move left, incrementing the index.

Decimal = Σ (bit × 2^index)

Example: (1101)■ → ?
Index: 3 2 1 0 (right to left)

Bit Manipulation — Complete Study Notes Page 2


Bits: 1 1 0 1
Calculation: 1×2³ + 1×2² + 0×2¹ + 1×2■ = 8 + 4 + 0 + 1 = 13

Code: Binary String → Decimal


o o o Code

int convert2Decimal(string x) {
int len = [Link]();
int p2 = 1; // starts at 2^0 = 1
int num = 0;
for (int i = len-1; i >= 0; i--) { // start from RMB
if (x[i] == "1") // add only if bit is 1
num = num + p2;
p2 = p2 * 2; // increase 2-multiplier
}
return num;
}

■ T: O(len) | S: O(1)

Bit Manipulation — Complete Study Notes Page 3


How Computers Store Integers & Complements
02
Computer storage of int x = 13

An int is 32 bits. The rightmost 4 bits hold 13 (1101), the remaining 28 bits are filled with 0.

long/long → 64 bits. The 31st bit (sign bit) is reserved for sign: 0 = positive, 1 = negative.

1■■ Complement
Step 1: Write the number in binary.
Step 2: Flip all bits (0→1, 1→0).
Example: (13)■■ = (1101)■ → flip → (0010)■

2■ Complement
Step 1: Find the 1■■ complement (flip all bits).
Step 2: Add 1 to the result.
Example: 13 → (1101)■ → flip → (0010)■ → +1 → (0011)■

Why 2's complement?

Computers store negative numbers using 2's complement. For -13:

① Take binary of 13: (0...01101)

② Find its 2's complement: flip → (1...10010), then +1 → (1...10011)

③ The leading '1' in the sign bit indicates it's negative.

The same result is also 2's complement. Negative numbers always have 1 as their sign bit.

Largest & Smallest Integers


Value Binary Pattern Decimal

INT_MAX 0 11111...1 (31 ones) 2³¹ - 1 = 2,147,483,647

INT_MIN 1 00000...0 (31 zeros) -2³¹ = -2,147,483,648

Important: NOT (~) operator

~x = -(x+1) in signed integers. So ~5 = -6.

Why? ~5 flips all bits, giving the 1s complement. Add the sign → -(5+1) = -6.

And ~(-6) = 5 — they are inverses!

Bit Manipulation — Complete Study Notes Page 4


Bitwise Operators: AND, OR, XOR
03
AND (&) Both bits 1 → 1; else 0 Selects/Extracts bits

OR (|) Any bit 1 → 1; both 0 → 0 Sets bits

XOR (^) Odd # of 1s → 1; even # → 0 Toggles bits / detects difference

NOT (~) Flips all bits ~x = -(x+1)

Worked Examples
AND: x = 13 & 7 = 5
o o o Code

1101 (13)
& 0111 (7)
■■■■■■
0101 = 5

Why AND = 5?

Only bits that are 1 in BOTH numbers stay as 1. The bit at position 3 in 13 (=8) has no matching 1 in 7,
so it gets cleared.

OR: x = 13 | 7 = 15
o o o Code

1101 (13)
| 0111 (7)
■■■■■■
1111 = 15

XOR: x = 13 ^ 7 = 10
o o o Code

01101 (13)
^ 00111 (7)
■■■■■■■■
01010 = 8 + 2 = 10

XOR Key Properties (Must Know!)

① a ^ a = 0 (same number XOR itself = 0)

② a ^ 0 = a (XOR with 0 returns same number)

③ XOR of odd count of 1s → 1; even count → 0

④ Commutative & Associative: a^b = b^a and (a^b)^c = a^(b^c)

⑤ Used to SWAP two numbers without a third variable (see Section 6)

Bit Manipulation — Complete Study Notes Page 5


Shift Operators: << and >>
04
RIGHT SHIFT » (Divide by 2^k)
x >> k = x / 2^k
The rightmost k bits are dropped off the cliff. Empty bits on the left are filled with 0 (for positive numbers).

Expression Binary before Binary after Decimal

13 » 1 00001101 00000110 6 (=13/2)

13 » 2 00001101 00000011 3 (=13/4)

13 » 4 00001101 00000000 0 (=13/16)

Pattern observation (Right Shift)

When we do 13»1: (1101)→(110), we notice:

13 = 1×2³+1×2²+0×2¹+1×2■

6 = 1×2²+1×2¹+0×2■ — same bits but each power reduced by 1!

The change is just extra multiples of 2 being removed. Hence x»k = x/2^k.

LEFT SHIFT « (Multiply by 2^k)


num << k = num × 2^k
Bits shift left. The leftmost bits fall off. Empty spots on the right are filled with 0.
o o o Code

13 << 1 = 26
// 00001101 → 00011010 = 26 = 13 × 2

// The new bits gain extra powers of 2: 0×2■ + 0×2■ + 1×2■ + 1×2³ + 0×2² + 1×2¹ +
0×2■
// Pattern: num << k = num × 2^k

■ Overflow Warning

(2³¹ - 1) « 1 → OVERFLOW! When leftmost bit shifts off the sign bit, the result wraps around to a large
negative number.

Always check: if shifting left by k bits, make sure you have room.

Binary Search optimization using shift

Instead of (low + high) / 2, use (low + high) » 1

Why? Division can overflow if low + high exceeds INT_MAX. Right shift handles it safely.

T: O(log N) worst if n = 2³¹

Bit Manipulation — Complete Study Notes Page 6


Must-Know Bit Manipulation Tricks
05
TRICK 1 — Swap Two Numbers Without a Temp Variable
Classic: a=5, b=6. Without using any third variable, swap them.

Why XOR works for swap

Recall: a^a=0 and a^0=a

Step 1: a = a^b (a now holds combined info)

Step 2: b = a^b = (a^b)^b = a^(b^b) = a^0 = a ✓

Step 3: a = a^b = (a^b)^a = b^(a^a) = b^0 = b ✓

o o o Code

a = a ^ b; // step 1
b = a ^ b; // step 2: b gets original a
a = a ^ b; // step 3: a gets original b

■ Important: This method fails if a and b point to the SAME memory location. Always prefer temp variable in
practice.

TRICK 2 — XOR with 0 returns same number, XOR with itself = 0


5 ^ 5 = 0 | 5 ^ 0 = 5
Used in: Single Number I, XOR in range problems

General Note on Bit Manipulation Speed


Generally, bit manipulation is NOT faster than generic way — UNLESS the operation itself is O(log
n) or O(n) in the generic approach but O(1) in bit manipulation.

Example: Checking power of 2 is O(log n) naively but O(1) with bits.

For most problems till now: T → O(1)

Bit Manipulation — Complete Study Notes Page 7


Operations on the i-th Bit
06
Check i-th bit
Is bit i set (1) or not (0)?
o o o Code

Brute Force: Convert to binary, traverse to bit i → O(log n)

// Optimal Approach 1: LEFT SHIFT


if (N & (1 << i)) != 0) return true; // bit is set
else return false; // not set

// How: (1<<i) places a 1 right below the i-th bit


// AND with N: if bit i is 1 → result != 0; if 0 → result = 0

// Optimal Approach 2: RIGHT SHIFT


// Move i-th bit to position 0, then AND with 1
if ((N >> i) & 1 == 0) → not set
else → set

Set i-th bit


Force bit i to 1
o o o Code

// Place a 1 right below the i-th bit using (1<<i)


// Perform OR operation
N | (1 << i)

// Why OR? At the i-th bit: 0 OR 1 = 1, 1 OR 1 = 1


// Other bits: 0 in (1<<i) → OR does NOT change original bits

// Example: N=9 (1001), i=2


// 1001
// |0100 <- (1<<2)
// 1101 = 13
// If already set, it does NOT alter it.

Clear i-th bit


Force bit i to 0 (unset it)
o o o Code

// We need 0 at position i, 1 at all others


// Create: ~(1<<i) — flip all bits of (1<<i)
// (1<<2) = 00100 → ~(1<<2) = 11011
// AND with N: other places have 1, so AND transfers them
// At i-th bit: if 1 → 1 & 0 = 0 ✓ if 0 → 0 & 0 = 0 ✓
N & ~(1 << i)

// T → O(1) for most problems

Bit Manipulation — Complete Study Notes Page 8


Toggle i-th bit
Flip bit i (0→1, 1→0)
o o o Code

// Use XOR operator


// Place 1 right below i-th bit and XOR
N ^ (1 << i)

// Why XOR? At i-th bit:


// if bit == 0 → 0 XOR 1 = 1 (changed to 1) ✓
// if bit == 1 → 1 XOR 1 = 0 (changed to 0) ✓
// Other places: no. of ones unchanged, bits transferred

// Example: N=13 (1101), i=2


// 1101
// ^ 0100
// 1001 → 9

Check: if((N>>i)&1==0) | Set: N|(1<<i) | Clear: N&~(1<<i) |


Toggle: N^(1<<i)
All T → O(1)

Bit Manipulation — Complete Study Notes Page 9


Remove Last Set Bit & Check Power of 2
07
REMOVE LAST SET BIT (Rightmost)
N & (N - 1)
Turns off the rightmost set bit

Observation: When you subtract 1 from N, the last set bit flips to 0 and all bits to its right become 1.
AND with original N: last set bit and below become 0 (since N-1 has 0 there now), other bits unchanged.

N Binary of N N-1 Binary N & (N-1)

12 1100 1011 1000 = 8

13 1101 1100 1100 = 12

16 10000 01111 00000 = 0

40 101000 100111 100000 = 32

Pattern in N and N-1:


The last set bit of N becomes 0, and all bits to its right become 1 (the exact inverse of N for those
positions).

CHECK IF NUMBER IS A POWER OF 2


Key observation

If N is a power of 2, it has EXACTLY ONE set bit. Examples:

16 = 10000 (1 set bit), 32 = 100000 (1 set bit)

Using N & (N-1): removes the last set bit. If N is power of 2, only one bit exists → result = 0.

if (N & (N-1) == 0) → Power of 2 ✓ else → NOT power of 2


T → O(1)

o o o Code

bool isPowerOf2(int N) {
return (N > 0) && ((N & (N-1)) == 0);
// N>0 handles edge case N=0 which would give false positive
}

Bit Manipulation — Complete Study Notes Page 10


Count the Number of Set Bits
08
BRUTE FORCE — Check Each Bit
o o o Code

int countSetBits_BF(int n) {
int cnt = 0;
while (n > 1) {
cnt += (n & 1); // instead of n%2==1, use (n&1)
n = n >> 1; // instead of n=n/2, use n>>1 (faster!)
}
if (n == 1) cnt++; // handle last bit
return cnt;
}

■ T: O(log n) | S: O(1)
Why use (n&1) instead of (n%2==1)? Because & is a hardware instruction — faster than modulo.
Also: n»1 is faster than n/2 for the same reason.

OPTIMAL — Brian Kernighan Algorithm


Brian Kernighan's key insight

N & (N-1) turns off the LAST set bit.

So each iteration removes one set bit. We count how many times we can do this before N reaches 0.

T → O(number of set bits) = O(31) worst case = O(1)!

o o o Code

int countSetBits(int n) {
int cnt = 0;
while (N != 0) {
N = N & (N-1); // remove last set bit
cnt++;
}
return cnt;
}

// T → O(No. of set bits) = O(31) if N is INT_MAX


// S → O(1)

Walkthrough — N = 84 (1010100)
Step N (binary) N & (N-1) cnt

1 1010100 = 84 1010000 = 80 1

2 1010000 = 80 1000000 = 64 2

3 1000000 = 64 0000000 = 0 3

Result: 3 set bits in 84. ✓ (84 = 64+16+4 = 3 powers of 2)

Bit Manipulation — Complete Study Notes Page 11


Minimum Bit Flips to Convert start → goal
09
Problem

Given start=10 (1010) and goal=7 (0111), find the minimum number of bit flips needed to convert start
to goal.

Key Insight: XOR gives 1 wherever bits differ


XOR of start and goal marks all positions where they differ — those are exactly the bits we need to flip!
o o o Code

start ^ goal → 1010 ^ 0111 = 1101 (3 set bits)


// Number of set bits in (start^goal) = minimum flips needed

ans = start ^ goal;


cnt = countSetBits(ans); // use Brian Kernighan!
return cnt;

Why does XOR work here?


XOR gives 1 only where the two bits are different. So every 1 in (start^goal) represents a position that
needs to be flipped.

Answer = popcount(start ^ goal)


popcount = count set bits

Alternative: Loop through all 32 bits

ans = start ^ goal

cnt = 0

for (i = 0 → 31): if (ans & (1«i)) → cnt++

return cnt

T → O(31) = O(1) | S → O(1)

Bit Manipulation — Complete Study Notes Page 12


Single Number I — Find the Unique Element
10
Problem

Given an array where every element appears TWICE except one. Find that one. Range: -10■ ≤ nums[i]
≤ 10■

Example: [4, 1, 2, 1, 2] → return 4

Approach 1 (Brute Force) — HashMap


Store count of each number in a map. Traverse and find element with count = 1.
T: O(n log n) + O(m) | S: O(m) where m = distinct elements

Approach 2 (Optimal) — XOR


XOR magic: cancel pairs

Since every element appears twice, XOR all elements together.

Pairs cancel: a^a = 0. Only the single element survives.

XOR is commutative, so order does not matter.

4^1^2^1^2 = 4^(1^1)^(2^2) = 4^0^0 = 4 ✓

o o o Code

int singleNumber(vector<int>& nums) {


int xorr = 0;
for (int i = 0; i < n-1; i++)
xorr = xorr ^ nums[i];
return xorr;
}
// T → O(N) | S → O(1)

Bit Manipulation — Complete Study Notes Page 13


Single Number II — Every element 3x except one
11
Problem

Given an array where every element appears THRICE except one. Find that one.

Example: [5,5,5,2,4,4,4] → return 2

Approach 1 — HashMap: O(n log n) time, O(n) space


Approach 2 — Sorting: sort, check triplets → O(N log N)
Approach 3 (Better) — Compare Each Bit of All Numbers
For each bit position (0→31), count how many numbers have that bit set.
If count % 3 ≠ 0, then the single element has that bit set.
■ Why? If all elements appear 3 times, bit count at every position is divisible by 3. The extra 1s come only
from the single element.
o o o Code

int singleNumber(vector<int>& nums) {


int ans = 0;
for (int bitIndex = 0; bitIndex <= 31; bitIndex++) {
int cnt = 0;
for (int i = 0; i < n-1; i++)
if (nums[i] & (1 << bitIndex)) cnt++;
if (cnt % 3 == 1) // this bit belongs to single element
ans = ans | (1 << bitIndex); // set it in answer
}
return ans;
}
// T → O(N×32) | S → O(1)

Approach 4 (Optimal) — Buckets (ones & twos)


Bucket Concept

ones: stores element if it appears odd times so far (appears once currently)

twos: stores element if it appears twice so far

threes: bucket (but we delete from ones & twos, so no need to track threes)

Condition: if nums[i] is in ones AND it appears again → move to twos

Condition: if nums[i] is in twos AND appears again → delete from twos (appeared 3× = gone)

o o o Code

// Add to ones if not in twos: ones = (ones ^ nums[i]) & ~twos


// Add to twos if was in ones: twos = (twos ^ nums[i]) & ~ones
int ones = 0, twos = 0;
for (int i = 0; i < n-1; i++) {
ones = (ones ^ nums[i]) & ~twos;

Bit Manipulation — Complete Study Notes Page 14


twos = (twos ^ nums[i]) & ~ones;
}
return ones; // single element survives in "ones"
// T → O(N) | S → O(1)

Bit Manipulation — Complete Study Notes Page 15


Single Number III — Two Elements Appear Once
12
Problem

Array where every element appears TWICE except TWO numbers. Find both.

Example: [2,4,2,6,3,7,7,3] → return [4, 6] (in any order)

Strategy: XOR + Buckets


Step 1: XOR all elements → remaining = a^b (a and b are our two unique numbers)
Step 2: a and b must differ by at least 1 bit (they are distinct). Find that differing bit.
Step 3: Use that bit to separate all numbers into 2 buckets. Each bucket's XOR = one unique number.

o o o Code

long long xorr = 0;


for (int i = 0; i < n; i++) xorr ^= nums[i];

// Find rightmost set bit (where a and b differ)


long long rightmost = (xorr & (xorr - 1)) ^ xorr;
// Note: use long to avoid overflow if nums[i] = -2^31

long long b1 = 0, b2 = 0; // two buckets


for (int i = 0; i < n; i++) {
if (nums[i] & rightmost) b1 ^= nums[i]; // bit is set → bucket 1
else b2 ^= nums[i]; // bit not set → bucket 2
}
return {b1, b2};
// T → O(2N) | S → O(1)

Why find the rightmost set bit?

a^b has 1s at all positions where a and b differ. We pick ANY one of these positions (rightmost for
simplicity).

All numbers with this bit set go to b1; others to b2.

The two unique numbers land in different buckets. Pairs XOR to 0 in their bucket. So each bucket XOR
= unique number.

Bit Manipulation — Complete Study Notes Page 16


Power Set — Generate All Subsets
13
Problem

Given nums = [1, 2, 3], generate all 2■ subsets: [], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3]

Key Insight: Binary representation of numbers 0 → 2■-1


For N=3 items, there are 2³=8 subsets. Number each subset 0 to 7.
The binary representation of each number tells you which items to include: bit i = 1 means include nums[i].

Number Binary (bit2 bit1 bit0) Subset

0 0 0 0 []

1 0 0 1 [1] (bit0 set → take nums[0])

2 0 1 0 [2] (bit1 set → take nums[1])

3 0 1 1 [1,2]

4 1 0 0 [3]

5 1 0 1 [1,3]

6 1 1 0 [2,3]

7 1 1 1 [1,2,3]

0 = Not Take, 1 = Take

o o o Code

vector<vector<int>> powerSet(vector<int>& nums) {


int n = [Link]();
int subsets = 1 << n; // = 2^n
vector<vector<int>> ans;
for (int num = 0; num < subsets; num++) { // 0 to 2^n-1
vector<int> list;
for (int i = 0; i < n; i++) { // check each bit
if (num & (1 << i)) // if bit i is set
[Link](nums[i]); // include nums[i]
}
[Link](list);
}
return ans;
}
// T → O(N × 2^N) | S → O(2^N × N)

Bit Manipulation — Complete Study Notes Page 17


XOR of Numbers in Given Range
14
Part 1: XOR from 1 to N
Problem

Given N, find XOR of all numbers 1, 2, 3, ..., N

Example: N=4 → 1^2^3^4 = (1^2)^(3^4) = 3^7 = 4

Brute Force: Loop 1 to N, XOR each → T: O(N)

Optimal: Pattern in XOR of 1 to N:


N XOR(1..N) N%4 Pattern

1 1 1 return 1

2 3 2 return N+1

3 0 3 return 0

4 4 0 return N

5 1 1 return 1

6 7 2 return N+1

7 0 3 return 0

8 8 0 return N

o o o Code

int func(int N) {
if (N % 4 == 1) return 1;
if (N % 4 == 2) return N + 1;
if (N % 4 == 3) return 0;
return N; // N % 4 == 0
}
// T → O(1) | S → O(1)

Part 2: XOR of numbers in range [L, R]


Use prefix XOR: XOR(L..R) = XOR(1..R) ^ XOR(1..L-1)
Why? XOR is self-inverse — elements before L appear in both and cancel out.
o o o Code

int xorRange(int L, int R) {


return func(L-1) ^ func(R);
}
// T → O(1) | S → O(1)

Bit Manipulation — Complete Study Notes Page 18


Divide Two Integers without × or ÷
15
Problem

Divide dividend by divisor without using multiplication (*) or division (/) operators.

Result must be in range [-2³¹, 2³¹-1]. Example: 22 ÷ 3 = 7

Edge case: if dividend = -2³¹ and divisor = -1, result = 2³¹ which overflows → return INT_MAX

Brute Force — Keep Subtracting


o o o Code

int divide_BF(int n, int d) {


int sum = 0, cnt = 0;
while (sum + d <= n) {
sum += d;
cnt++;
}
return cnt;
}
// T → O(dividend) → TLE for large inputs!

Optimal — Binary Powers of Divisor


Key Insight

Instead of subtracting d once at a time, subtract d×2^k at each step (the largest multiple of d that fits).

Example: 22 ÷ 3. Can we remove 3×2²=12? Yes (22≥12). Can we remove 3×2³=24? No.

So: 22 - 12 = 10, ans += 4. Then 10 - 6 = 4, ans += 2. Then 4 - 3 = 1, ans += 1. Total = 7.

o o o Code

long divide(long n, long d) {


int sign = 1;
if ((n>0 && d<0) || (n<0 && d>=0)) sign = -1;
n = abs(n); d = abs(d);
long ans = 0;
while (n >= d) {
int cnt = 0;
while (n >= (d << (cnt+1))) cnt++; // find largest power
ans += (1 << cnt); // add 2^cnt to answer
n -= d * (1 << cnt); // same as d × 2^cnt
}
// Handle overflow edge cases
if (ans >= 2^31 && sign == true) return INT_MAX;
if (ans >= 2^31 && sign == false) return INT_MIN;
return sign ? ans : (-1 * ans);

Bit Manipulation — Complete Study Notes Page 19


}
// T → O(log■N)² | S → O(1)

Bit Manipulation — Complete Study Notes Page 20


Advanced Maths: Prime Factors of a Number
16
Problem

Find all PRIME factors of N. Example: N=60 → return [2, 3, 5]

(60 = 2×2×3×5, but only unique primes: 2, 3, 5)

Brute Force
o o o Code

for (i=0; i<=N; i++) {


if (n%i == 0 && isPrime(i))
[Link](i);
}
// T → O(N × √N) | S → O(7) ≈ O(1)

Better Approach — Factor out each prime


We don't need to check isprime separately! Just iterate i=2 to √N:
If i divides N, divide N by i completely (while N%i==0, N=N/i), add i to list.
After the loop, if N>1, then N itself is a large prime — add N.
o o o Code

for (int i = 2; i*i <= N; i++) {


if (N % i == 0) {
[Link](i);
while (N % i == 0) N = N/i; // remove all factors of i
}
}
if (N > 1) [Link](N); // remaining N is a prime > √N
// T → O(√N × log N) | S → O(1)

// More optimal: for(i=2; i<=N; i++) — same logic, T→O(√N × logN)

Why only check up to √N?

If N has a factor larger than √N, the corresponding co-factor must be smaller than √N — and we would
have already found it.

Example: N=36. √36=6. Factors: 2,3,4,6 (all ≤6) and 9,12,18,36 (>6 but paired with 4,3,2,1).

Optimal for Multiple Queries — SPF (Sieve of Smallest Prime Factors)


Precompute SPF[i] = smallest prime factor of i for all i up to 10■.
o o o Code

// Precompute SPF
for (i = 1 → 10^5) spf[i] = i;
for (i = 2; i*i <= 10^5; i++) {
if (spf[i] == i) { // i is prime
for (j = i*i; j <= 10^5; j += i)

Bit Manipulation — Complete Study Notes Page 21


if (spf[j] == j) spf[j] = i;
}
}
// For each query n, factorize in O(log n):
while (n != 1) { print(spf[n]); n = n / spf[n]; }
// T → O(N log log N + Q log N) | S → O(N)

Bit Manipulation — Complete Study Notes Page 22


Sieve of Eratosthenes — All Primes till N
17
Problem

Given N, find all prime numbers from 2 to N.

Example: N=10 → [2, 3, 5, 7]

Brute Force: Check each i from 2 to N if prime → T: O(N × √N)

Optimal: Sieve of Eratosthenes


① Mark all numbers 2 to N as prime (1).
② Start at i=2. Mark all multiples of 2 (from 2×2) as not prime (0).
③ Move to next unmarked number, repeat.
④ Key optimization: start marking from i×i (not 2×i), since smaller multiples already marked.
⑤ Only go up to √N — if i×i > N, no new composites to mark.
o o o Code

void sieve(int N) {
bool prime[N+1];
fill(prime, prime+N+1, true);
prime[0] = prime[1] = false;
for (int i = 2; i*i <= N; i++) {
if (prime[i]) {
for (int j = i*i; j <= N; j += i)
prime[j] = false; // mark composite
}
}
// Collect primes
for (int i = 2; i <= N; i++)
if (prime[i]) [Link](i);
}
// T → O(N log(log N) + O(N)) ≈ O(N log log N)
// S → O(N)

Why start from i×i?

For prime i, all multiples i×2, i×3, ..., i×(i-1) were already marked by earlier primes.

The first unmarked multiple of i must be i×i. This optimization reduces marking redundancy.

Bit Manipulation — Complete Study Notes Page 23


Power Exponentiation — Fast Power (pow(x, n))
18
Problem

Compute x^n efficiently. Example: pow(2, 5) = 32

Range: x can be up to 10■, n can be large. Must handle negative n.

Brute Force: Multiply x, n times → T: O(N)

Optimal: Binary Exponentiation


Key Insight — Use binary representation of n

x^20 = x^(10+10) = x^10 × x^10 → just 1 multiplication, not 10!

x^21 = x × x^20 → if n is odd, extract one x and solve x^(n-1)

If n is even: x^n = (x²)^(n/2) → square x, halve n

Each step halves n → only O(log n) steps!

o o o Code

double myPow(double x, long long n) {


if (n < 0) { x = 1.0/x; n = -n; } // handle negative exponent
double ans = 1.0;
while (n > 0) {
if (n % 2 == 1) { // n is odd → same as (n&1) == 1
ans = ans * x;
n = n - 1;
} else { // n is even
n = n / 2; // same as n >>= 1
x = x * x; // square x
}
}
return ans;
}
// T → O(log N) | S → O(1)

Dry Run: x=16, n=5 (odd → n-1=4, x same, ans×=16)


Step x n ans n odd/even

Start 16 5 1 odd

1 16 4 16 even

2 256 2 16 even

3 65536 1 16 odd

16×65536=10485
4 65536 0 76 stop

Final ans = 32 × 65536 = 1,048,576 = 16■ ✓ (just 4 steps instead of 5)

Bit Manipulation — Complete Study Notes Page 24


Quick Reference — All Bit Tricks at a Glance

Operation Code Notes

Check i-th bit (N»i) & 1 OR if(N&(1«i)) 0=not set, 1=set

Set i-th bit N | (1«i) Force bit to 1

Clear i-th bit N & ~(1«i) Force bit to 0

Toggle i-th bit N ^ (1«i) Flip 0↔1

Remove last set bit N & (N-1) Rightmost 1 cleared

Power of 2 check N>0 && (N&(N-1))==0 T→O(1)

Count set bits while(N){N=N&(N-1);cnt++} Brian Kernighan

Swap a,b a^=b; b^=a; a^=b; No temp var needed

Right shift x » k = x / 2^k Drop k LSBs

Left shift x « k = x × 2^k Add k zero LSBs

XOR all → single no. xorr ^= nums[i] for all i Pairs cancel

Min flips to convert popcount(start ^ goal) XOR marks differences

Power Set for num in 0..(1«n)-1 T→O(N×2^N)

XOR 1..N Pattern on N%4 T→O(1)

Fast Power Binary exponentiation T→O(log N)

Prime factors Loop i=2 to √N T→O(√N × logN)

All primes till N Sieve of Eratosthenes T→O(N log log N)

General rule: bit manipulation is O(1) for single-number ops,


O(N) for array ops
Use (n&1) instead of n%2, use n>>1 instead of n/2 — both are faster!

Bit Manipulation — Complete Study Notes Page 25

You might also like