150 problems across 14 chapters. Every problem has a plain-English statement, a clear approach that teaches how to think, a dry run on a real example, clean verified C++ code, and an insight on complexity. This is the file you keep open when you practice.
Given a number n, determine if it is odd or even.
n = 7OddKey insight: A number is even if divisible by 2 — remainder is 0. The modulus operator % gives the remainder. If n % 2 == 0 → even, else → odd.
Bit trick: The last bit of a number is 1 if odd, 0 if even. n & 1 is faster than modulus and avoids the negative-modulo edge case.
7 % 2 = 1 (remainder ≠ 0) → Odd 7 & 1 = 1 (last bit is 1) → Odd
#include<bits/stdc++.h>
using namespace std;
int main(){
int n; cin>>n;
cout << ((n&1) ? "Odd" : "Even") << "\n";
}Time: O(1) | Space: O(1). Prefer n & 1 over n % 2 in competitive programming — it handles negative numbers correctly and is one CPU instruction.
Swap the values of two variables a and b.
a=5, b=9a=9, b=5Temp variable: Store a in temp, put b in a, put temp in b. Safest, most readable — use in interviews.
Arithmetic: a=a+b; b=a-b; a=a-b. Clever but risks overflow on large integers.
XOR: a^=b; b^=a; a^=b. No extra space, no overflow. Caution: breaks if a and b are the same memory location.
a ^= b → a = 0101 ^ 1001 = 1100 (12) b ^= a → b = 1001 ^ 1100 = 0101 (5) a ^= b → a = 1100 ^ 0101 = 1001 (9) Result: a=9, b=5
int a=5, b=9;
// Method 1: temp (safest, use this)
int temp=a; a=b; b=temp;
// Method 2: XOR (no extra space)
a^=b; b^=a; a^=b;
// Method 3: STL (best in real code)
swap(a,b);Time: O(1) | Space: O(1). Never use XOR swap on swap(a[i], a[i]) — swapping an element with itself via XOR yields 0 and destroys the value.
Find the sum of all digits of a given number n.
n = 123410n % 10 gives the last digit. n / 10 removes it. Repeat until n becomes 0, accumulating digits into a sum.
n=1234: digit = 1234%10 = 4, sum= 4, n=123 n= 123: digit = 123%10 = 3, sum= 7, n= 12 n= 12: digit = 12%10 = 2, sum= 9, n= 1 n= 1: digit = 1%10 = 1, sum=10, n= 0 Loop ends → Answer: 10
int sumDigits(int n){
n = abs(n); // handle negatives
int sum=0;
while(n>0){
sum += n%10; // add last digit
n /= 10; // remove last digit
}
return sum;
}Time: O(d) where d = number of digits | Space: O(1). The digit-extraction pattern (%10 + /10) is the foundation of dozens of number-theory problems.
Reverse the digits of an integer. Handle negatives. Example: 1234 → 4321, −56 → −65.
Same digit-extraction loop. Build the reversed number with: rev = rev * 10 + digit. Each iteration shifts existing digits left one place, then appends the new digit at the ones position.
rev= 0: digit=4, rev = 0*10+4 = 4 rev= 4: digit=3, rev = 4*10+3 = 43 rev= 43: digit=2, rev = 43*10+2 = 432 rev=432: digit=1, rev = 432*10+1= 4321 n=0 → Answer: 4321
int reverseDigits(int n){
bool neg = (n<0); n=abs(n);
long long rev=0; // long long avoids overflow
while(n>0){
rev = rev*10 + n%10;
n /= 10;
}
return neg ? -rev : rev;
}Time: O(d) | Space: O(1). Use long long for the reversed result — reversing a number near INT_MAX can overflow a 32-bit int.
Count the number of digits in a number n. For n = 4567, answer is 4.
Loop: Divide by 10 repeatedly, count iterations. O(d).
Math: floor(log10(n)) + 1. O(1), but undefined for n=0 — handle separately.
String: to_string(n).length(). Easy to read, slight overhead.
n=4567 → cnt=1, n=456
n= 456 → cnt=2, n= 45
n= 45 → cnt=3, n= 4
n= 4 → cnt=4, n= 0
Loop ends → 4 digitsint countDigits(int n){
if(n==0) return 1;
n = abs(n);
int cnt=0;
while(n>0){ cnt++; n/=10; }
return cnt;
}
// Math: (int)log10(abs(n)) + 1 [n != 0]
// String: to_string(abs(n)).size()Time: O(d) loop / O(1) math | Space: O(1). The math trick is O(1) but floating-point precision can bite — always guard against n=0.
Given a, b, c — evaluate (a+b) mod c and (a×b) mod c without overflow. Key property: (a+b)%c == (a%c + b%c) % c.
If a and b are each up to 10⁹, then a×b overflows a 64-bit integer. Apply mod before multiplying: (a%c * b%c) % c. This is the foundation of every modular arithmetic problem in competitive programming.
(a+b)%c = 15%3 = 0 (a%c + b%c)%c = (1+2)%3 = 0 ✓ same result (a*b)%c = 50%3 = 2 ((a%c)*(b%c))%c = (1*2)%3 = 2 ✓ same result
long long a,b,c; cin>>a>>b>>c;
cout << (a%c + b%c) % c << "\n"; // (a+b) mod c
cout << (a%c * (b%c)) % c << "\n"; // (a*b) mod cTime: O(1) | Space: O(1). Modular arithmetic is used in almost every combinatorics or large-number problem. The constant MOD = 1e9+7 is a prime — memorize it.
Find the GCD and LCM of two numbers. GCD is the largest number that divides both. LCM is the smallest number divisible by both.
Rule: GCD(a, b) = GCD(b, a mod b). Repeat until the second argument is 0 — the first is the answer.
Why it works: Any divisor of both a and b also divides (a mod b). The GCD is preserved at every step.
LCM: LCM(a,b) = a / GCD(a,b) * b. Divide first to prevent overflow.
GCD(48, 18) → GCD(18, 48%18=12) GCD(18, 12) → GCD(12, 18%12= 6) GCD(12, 6) → GCD( 6, 12% 6= 0) GCD( 6, 0) → 6 (second arg = 0, answer is first)
int gcd(int a, int b){
return b==0 ? a : gcd(b, a%b);
}
int lcm(int a, int b){
return a/gcd(a,b)*b; // divide first to avoid overflow
}
// C++17 built-ins: __gcd(a,b) | std::lcm(a,b) from <numeric>Time: O(log min(a,b)) | Space: O(log min(a,b)) stack. The iterative version (while b: a,b = b, a%b) uses O(1) space and is preferred in tight loops.
Determine if a number n is prime. A prime has exactly two divisors: 1 and itself.
Divisors come in pairs: if i divides n, so does n/i. One of the pair is always ≤ √n. So checking up to √n is complete. Skip even numbers after handling 2 separately.
n=29, √29 ≈ 5.38 → check i = 3, 5
i=3: 29%3 = 2 (not divisible)
i=5: 29%5 = 4 (not divisible)
i=7: 7*7=49 > 29 → loop ends
29 is Prime ✓bool isPrime(int n){
if(n<2) return false;
if(n==2) return true;
if(n%2==0) return false;
for(int i=3; i*i<=n; i+=2) // odd divisors only
if(n%i==0) return false;
return true;
}Time: O(√n) | Space: O(1). The i+=2 step halves iteration count by skipping even numbers — a simple but powerful optimization.
Find the count of all divisors of n. For n = 12, divisors are {1,2,3,4,6,12} → answer is 6.
Loop i from 1 to √n. When i divides n, both i and n/i are divisors. If i == n/i (perfect square root), count only once.
i=1: 36%1==0 → +2 (1, 36) cnt=2
i=2: 36%2==0 → +2 (2, 18) cnt=4
i=3: 36%3==0 → +2 (3, 12) cnt=6
i=4: 36%4==0 → +2 (4, 9) cnt=8
i=5: 36%5≠0 → skip
i=6: 36%6==0, i==n/i → +1 (6) cnt=9
i=7: 7*7=49 > 36 → loop ends
Total: 9 divisorsint countDivisors(int n){
int cnt=0;
for(int i=1; i*i<=n; i++){
if(n%i==0){
cnt++; // i is a divisor
if(i != n/i) cnt++; // n/i is another; skip if i==n/i
}
}
return cnt;
}Time: O(√n) | Space: O(1). Perfect squares have an odd number of divisors — the pair-counting trick naturally handles this with the i != n/i guard.
(A) Check if n is a perfect square. (B) Find the integer cube root of n (floor value).
Perfect square: Take r = (int)sqrt(n). Check both r and r+1 because floating-point can underestimate by 1.
Cube root: Binary search on [0, n]. Find the largest mid where mid³ ≤ n. Use long long since mid³ can overflow 32-bit int.
lo=0, hi=27
mid=13: 13³=2197 > 27 → hi=13
mid= 6: 6³= 216 > 27 → hi= 6
mid= 3: 3³= 27 ==27 → Answer: 3bool isPerfectSquare(int n){
if(n<0) return false;
int r = (int)sqrt(n);
return (r*r==n) || (r+1)*(r+1)==n; // guard FP error
}
int cubeRoot(int n){
int lo=0, hi=n;
while(lo<=hi){
long long mid = lo + (hi-lo)/2;
long long cube = mid*mid*mid;
if(cube == n) return mid;
else if(cube < n) lo = mid+1;
else hi = mid-1;
}
return hi; // floor cube root
}Time: O(log n) for cube root | Space: O(1). Never use pow(n, 1.0/3) for integer cube roots — floating-point errors will give wrong answers on numbers like 8, 27, 64.
(A) Simple Interest = P × R × T / 100. (B) Distance between points (x1,y1) and (x2,y2) = √((x2−x1)² + (y2−y1)²).
dx = 3-0 = 3, dy = 4-0 = 4
dx² + dy² = 9 + 16 = 25
√25 = 5double simpleInterest(double P, double R, double T){
return (P * R * T) / 100.0;
}
double euclidDist(double x1,double y1,double x2,double y2){
return hypot(x2-x1, y2-y1); // hypot handles overflow safely
}Time: O(1) | Space: O(1). Prefer hypot(dx, dy) over manual squaring — it avoids overflow when dx or dy is very large.
Given length l, breadth b, height h of a cuboid, find its Volume, Total Surface Area, and Lateral Surface Area.
Volume = l × b × h
Total Surface Area (TSA) = 2(lb + bh + hl)
Lateral Surface Area (LSA) = 2h(l + b) — the four walls only, no top/bottom
Volume = 2×3×4 = 24 TSA = 2(2×3 + 3×4 + 4×2) = 2(6+12+8) = 52 LSA = 2×4×(2+3) = 8×5 = 40
long long l,b,h; cin>>l>>b>>h;
cout << "Volume = " << l*b*h << "\n";
cout << "TSA = " << 2*(l*b + b*h + h*l) << "\n";
cout << "LSA = " << 2*h*(l+b) << "\n";Time: O(1) | Space: O(1). Use long long if l, b, h can exceed 10⁴ — the product l×b×h can exceed INT_MAX silently.
Given a number entered as a decimal, check if all its digits are only 0 or 1. Example: 1011 → Yes, 1023 → No.
Extract each digit via n%10. If any digit is not 0 or 1, return false immediately. Use long long since binary representations of large numbers can exceed int range.
digit = 1023%10 = 3 3 ≠ 0 and 3 ≠ 1 → Not binary ✗
bool isBinary(long long n){
if(n==0) return true;
while(n>0){
int d = n%10;
if(d!=0 && d!=1) return false;
n/=10;
}
return true;
}
// 1011 → digits 1,1,0,1 → all valid → trueTime: O(d) | Space: O(1). Digit-by-digit validation is always cleaner than converting to string for single-property checks.
(A) Print the binary representation of integer n. (B) Double and halve n using bit shifts.
Check each bit from position 31 down to 0 using (n >> i) & 1. Left shift (<<1) multiplies by 2. Right shift (>>1) divides by 2 (integer floor).
13 in binary: 1101 (13>>3)&1 = 1 (13>>2)&1 = 1 (13>>1)&1 = 0 (13>>0)&1 = 1 Output: 1101
// Print binary (skip leading zeros)
void printBinary(int n){
bool leading=true;
for(int i=31;i>=0;i--){
int bit=(n>>i)&1;
if(bit) leading=false;
if(!leading) cout<<bit;
}
if(leading) cout<<0; // n=0 edge case
cout<<"\n";
}
// Bit shifts
int doubled = n << 1; // n * 2
int halved = n >> 1; // n / 2 (floor)
// Cleaner: bitset prints fixed 8 bits
cout << bitset<8>(n) << "\n";Time: O(1) | Space: O(1). Bit shifts are single CPU instructions — much faster than division/multiplication. Left shift by k = multiply by 2ᵏ.
Determine if a year is a leap year. Rules: divisible by 4, BUT not by 100, UNLESS also divisible by 400.
Test in order: if ÷400 → leap. Else if ÷100 → not leap. Else if ÷4 → leap. Else → not leap. Compress to one boolean expression.
y=1900: %400≠0, %100==0 → NOT leap y=2000: %400==0 → LEAP y=2024: %400≠0, %100≠0, %4==0 → LEAP
bool isLeap(int y){
return (y%400==0) || (y%100!=0 && y%4==0);
}Time: O(1) | Space: O(1). This is the exact Gregorian calendar rule. The 400-year exception (2000 was a leap year) catches a common off-by-one that trips beginners.
A number is sparse if no two consecutive bits in its binary form are both 1. Example: 5 (101) is sparse; 6 (110) is not.
Shift n right by 1 and AND with original n. If any bit position is 1 in both, there are adjacent set bits. Result = 0 means no adjacent bits → sparse.
n=5: n = 101
n>>1 = 010
AND = 000 → Sparse ✓
n=6: n = 110
n>>1 = 011
AND = 010 → Not Sparse ✗bool isSparse(int n){
return (n & (n>>1)) == 0;
}Time: O(1) | Space: O(1). A single AND operation replaces an O(log n) bit-by-bit loop. This pattern — check adjacency using a shifted mask — appears in many bit-manipulation problems.
Find the greatest among three integers a, b, c.
a=9, b=4, c=99if-else: If a≥b and a≥c → a. Else if b≥c → b. Else c. Clear and readable.
max(): max({a, b, c}) with initializer list — cleanest in C++11+.
Ternary: a>b ? (a>c?a:c) : (b>c?b:c) — one line, tricky to read.
a>=b? 9>=4 Yes. a>=c? 9>=7 Yes. → Greatest = 9int a,b,c; cin>>a>>b>>c;
// Method 1: if-else (recommended for readability)
int greatest;
if(a>=b && a>=c) greatest=a;
else if(b>=c) greatest=b;
else greatest=c;
// Method 2: STL max (cleanest)
greatest = max({a,b,c});
cout << greatest << "\n";Time: O(1) | Space: O(1). max({a,b,c}) uses an initializer_list internally — it's O(n) comparisons but perfectly fine for small fixed sets.
Check if a given character is a vowel (a, e, i, o, u), case-insensitive.
'E'Voweltolower('E') = 'e'
"aeiou".find('e') found → Vowelbool isVowel(char c){
c = tolower(c);
return c=='a'||c=='e'||c=='i'||c=='o'||c=='u';
}
// Or: string("aeiou").find(tolower(c)) != string::nposTime: O(1) | Space: O(1). The tolower() call normalises the input — always do this before character classification to avoid duplicate branches for upper/lower case.
Convert a character or entire string between upper and lower case. Understand the ASCII relationship.
'A'=65, 'a'=97. The difference is exactly 32. So lowercase − 32 = uppercase. The XOR trick: c ^ 32 toggles case for any letter. Built-ins toupper() and tolower() are the standard approach.
'a' - 32 = 65 = 'A' (manual)
'a' ^ 32 = 65 = 'A' (XOR toggle)
toupper('a') = 'A' (stdlib)// Single character
char c='a';
cout << (char)toupper(c) << "\n"; // 'A'
cout << (char)(c^32) << "\n"; // 'A' (XOR toggle)
// Entire string
string s = "Hello World";
transform(s.begin(), s.end(), s.begin(), ::toupper);
// s is now "HELLO WORLD"Time: O(n) for string | Space: O(1). The XOR trick c ^ 32 only works on letters — never apply it to digits or punctuation or you'll get garbage characters.
Given a face value on a standard die, find the opposite face. Opposite faces sum to 7: (1↔6), (2↔5), (3↔4).
top = 347 - 1 = 6 ✓ 7 - 2 = 5 ✓ 7 - 3 = 4 ✓ 7 - 4 = 3 ✓ 7 - 5 = 2 ✓ 7 - 6 = 1 ✓
int oppositeFace(int top){ return 7 - top; }Time: O(1) | Space: O(1). Pattern recognition: when elements pair to a constant sum, always express the relationship as constant − value.
Replace every digit 0 in a number with 5. Example: 1020 → 1525.
Extract each digit. If it's 0, use 5 instead. Rebuild the number using positional arithmetic (multiply by place value).
d=0 →5, place=1: result += 5×1 = 5
d=2, place=10: result += 2×10 = 20
d=0 →5, place=100: result += 5×100= 500
d=1, place=1000:result += 1×1000=1000
Total = 1525int replace0with5(int n){
if(n==0) return 5;
int result=0, place=1;
while(n>0){
int d = n%10;
if(d==0) d=5;
result += d * place;
place *= 10;
n /= 10;
}
return result;
}Time: O(d) | Space: O(1). The rebuild pattern (accumulate into result with multiplied place value) is the reverse of digit extraction — master both.
(A) Sum 1+2+3+…+n. (B) Sum of sums: S(n) = (1) + (1+2) + (1+2+3) + … + (1+…+n).
Sum 1 to n: n×(n+1)/2 — Gauss formula. Constant time, no loop needed.
Sum of sums: The k-th inner sum is k×(k+1)/2. The total is n×(n+1)×(n+2)/6.
Sum 1..4 = 4×5/2 = 10 Sum of sums: 1 + 3 + 6 + 10 = 20 Formula: 4×5×6/6 = 20 ✓
long long sumN(int n){
return (long long)n*(n+1)/2;
}
long long sumOfSums(int n){
return (long long)n*(n+1)*(n+2)/6;
}Time: O(1) | Space: O(1). Whenever you see a loop that adds 1+2+…+n, replace it with the Gauss formula. Always cast to long long before multiplying to prevent overflow.
Print the multiplication table for a number n, from 1 to 10.
n = 55×1=5, 5×2=10, ..., 5×10=50int n; cin>>n;
for(int i=1; i<=10; i++)
cout << n << " x " << i << " = " << n*i << "\n";Time: O(1) — loop is always 10 iterations | Space: O(1). This is loop mechanics 101 — understand how the loop variable controls both the multiplier and the line count.
Print the first n Fibonacci numbers. Series: 0, 1, 1, 2, 3, 5, 8, 13, … Each term = sum of previous two.
Keep only two variables: a (current) and b (next). Each step: print a, then update a=b, b=a+b. No array needed.
a=0, b=1 → print 0, a=1, b=1
a=1, b=1 → print 1, a=1, b=2
a=1, b=2 → print 1, a=2, b=3
a=2, b=3 → print 2, a=3, b=5
a=3, b=5 → print 3, a=5, b=8
a=5, b=8 → print 5, a=8, b=13
a=8, b=13 → print 8
Output: 0 1 1 2 3 5 8void fibonacci(int n){
long long a=0, b=1;
for(int i=0; i<n; i++){
cout << a << " ";
long long next = a+b;
a = b;
b = next;
}
cout << "\n";
}Time: O(n) | Space: O(1). This iterative approach is far superior to the naive O(2ⁿ) recursive version. For n up to 1 million, use matrix exponentiation (see Ch 11).
(A) Sum of Arithmetic Progression: a, a+d, a+2d, … n terms. (B) Sum of Geometric Progression: a, ar, ar², … n terms.
AP Sum: S = n/2 × (2a + (n−1)d)
GP Sum: S = a × (rⁿ − 1) / (r − 1) for r≠1. If r=1: S = a×n.
S = 4/2 × (2×2 + (4−1)×3) = 2 × (4+9) = 26
Check: 2+5+8+11 = 26 ✓double apSum(double a, double d, int n){
return n/2.0 * (2*a + (n-1)*d);
}
double gpSum(double a, double r, int n){
if(r==1.0) return a*n;
return a * (pow(r,n)-1.0) / (r-1.0);
}Time: O(1) | Space: O(1). For the GP sum with large n, pow(r,n) can overflow doubles — in contest problems with modular constraints use fast power (see P110).
Find the n-th triangular number. Series: 1, 3, 6, 10, 15, 21… (each term = previous + n).
Term 1=1, Term 2=1+2=3, Term 3=1+2+3=6, Term n = 1+2+…+n = n(n+1)/2. It's just the sum of first n natural numbers.
5×6/2 = 15 ✓ (1+2+3+4+5 = 15)
long long triangular(int n){
return (long long)n*(n+1)/2;
}Time: O(1) | Space: O(1). Pattern recognition habit: always write the first 5–6 terms before coding. The pattern almost always reduces to a closed-form formula.
(A) Mean of an array. (B) Median — middle value of sorted array. (C) Running average: update the average as each new number arrives, without storing all numbers.
Instead of storing everything and recomputing: avg = avg + (newVal − avg) / count. This incrementally updates the average in O(1) per element with no overflow risk.
count=1, x=4: avg = 0 + (4-0)/1 = 4.00
count=2, x=8: avg = 4 + (8-4)/2 = 6.00
count=3, x=6: avg = 6 + (6-6)/3 = 6.00// Mean
double mean(vector<int>& a){
return accumulate(a.begin(),a.end(),0.0) / a.size();
}
// Median
double median(vector<int> a){
sort(a.begin(),a.end());
int n=a.size();
return (n%2) ? a[n/2] : (a[n/2-1]+a[n/2])/2.0;
}
// Running average (no array needed)
void runningAvg(){
double avg=0; int cnt=0, x;
while(cin>>x){
cnt++;
avg += (x - avg) / cnt;
cout << fixed << setprecision(2) << avg << "\n";
}
}Time: O(n log n) for median (sort), O(1) per element for running avg | Space: O(1) for running avg. Welford's formula is numerically stable — it won't accumulate floating-point errors like the naive sum-then-divide approach.
A 3-digit number n is fascinating if concatenating n, 2n, and 3n produces a 9-digit string containing every digit 1–9 exactly once. Example: 192 → "192384576" → Yes.
Concatenate n, 2n, 3n as strings. Check length is exactly 9. Sort the string and compare to "123456789".
n=192, 2n=384, 3n=576
concat = "192384576" (length 9 ✓)
sorted = "123456789" → Fascinating ✓bool isFascinating(int n){
string s = to_string(n) + to_string(2*n) + to_string(3*n);
if(s.size() != 9) return false;
sort(s.begin(), s.end());
return s == "123456789";
}
// Only 192, 219, 273, 327, 192... (few exist below 1000)Time: O(1) — string is always ≤9 chars | Space: O(1). When checking "contains all of X", convert and sort is simpler than a frequency array for small fixed alphabets.