Complete Edition · 150 Problems · File 08 of 08
C++ Programming

Problem Solving
Beginner to Advanced

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.

Ch 1 · Basic Math & Numbers Ch 2 · Decision Making Ch 3 · Loops & Series Ch 4 · Arrays Ch 5 · Strings Ch 6 · Patterns Ch 7 · Matrix Ch 8 · Intermediate DSA Ch 9 · OOP & Memory Ch 10 · Advanced Patterns Ch 11 · Number Theory Ch 12 · Two-Pointer & Sliding Window Ch 13 · STL & Comparators Ch 14 · Bitwise Sorcery
Chapter 01
Basic Math & Numbers — The Core Building Blocks

P01 Odd or Even Beginner

Given a number n, determine if it is odd or even.

Inputn = 7
OutputOdd
Approach

Key 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.

Dry Run: n = 7
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";
}
Insight & Complexity

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.

P02 Swap Two Numbers Beginner

Swap the values of two variables a and b.

Inputa=5, b=9
Outputa=9, b=5
Three Methods

Temp 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.

Dry Run XOR: a=5 (0101), b=9 (1001)
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);
Insight & Complexity

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.

P03 Sum of Digits Beginner

Find the sum of all digits of a given number n.

Inputn = 1234
Output10
Approach — Digit Extraction Loop

n % 10 gives the last digit. n / 10 removes it. Repeat until n becomes 0, accumulating digits into a sum.

Dry Run: n = 1234
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;
}
Insight & Complexity

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.

P04 Reverse Digits Beginner

Reverse the digits of an integer. Handle negatives. Example: 1234 → 4321, −56 → −65.

Approach

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.

Dry Run: n = 1234
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;
}
Insight & Complexity

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.

P05 Count Digits Beginner

Count the number of digits in a number n. For n = 4567, answer is 4.

Three Approaches

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.

Dry Run: n = 4567
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 digits
int 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()
Insight & Complexity

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.

P06 Modular Arithmetic Beginner

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.

Why the Distributive Property Matters

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.

Dry Run: a=10, b=5, c=3
(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 c
Insight & Complexity

Time: 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.

P07 GCD and LCM Beginner

Find the GCD and LCM of two numbers. GCD is the largest number that divides both. LCM is the smallest number divisible by both.

Approach — Euclid's Algorithm

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.

Dry Run: GCD(48, 18)
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>
Insight & Complexity

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.

P08 Prime Number Check Beginner

Determine if a number n is prime. A prime has exactly two divisors: 1 and itself.

Approach — Check up to √n

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.

Dry Run: n = 29
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;
}
Insight & Complexity

Time: O(√n) | Space: O(1). The i+=2 step halves iteration count by skipping even numbers — a simple but powerful optimization.

P09 Count Divisors Beginner

Find the count of all divisors of n. For n = 12, divisors are {1,2,3,4,6,12} → answer is 6.

Approach — Pair Counting up to √n

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.

Dry Run: n = 36
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 divisors
int 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;
}
Insight & Complexity

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.

P10 Perfect Square & Integer Cube Root Beginner

(A) Check if n is a perfect square. (B) Find the integer cube root of n (floor value).

Approach

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.

Dry Run: cubeRoot(27)
lo=0, hi=27
mid=13: 13³=2197 > 27  →  hi=13
mid= 6:  6³= 216 > 27  →  hi= 6
mid= 3:  3³=  27 ==27  →  Answer: 3
bool 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
}
Insight & Complexity

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.

P11 Simple Interest & Euclidean Distance Beginner

(A) Simple Interest = P × R × T / 100. (B) Distance between points (x1,y1) and (x2,y2) = √((x2−x1)² + (y2−y1)²).

Dry Run: Distance (0,0) → (3,4)
dx = 3-0 = 3,  dy = 4-0 = 4
dx² + dy² = 9 + 16 = 25
√25 = 5
double 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
}
Insight & Complexity

Time: O(1) | Space: O(1). Prefer hypot(dx, dy) over manual squaring — it avoids overflow when dx or dy is very large.

P12 Cuboid — Surface Area & Volume Beginner

Given length l, breadth b, height h of a cuboid, find its Volume, Total Surface Area, and Lateral Surface Area.

Formulas

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

Dry Run: l=2, b=3, h=4
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";
Insight & Complexity

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.

P13 Check Binary Number Beginner

Given a number entered as a decimal, check if all its digits are only 0 or 1. Example: 1011 → Yes, 1023 → No.

Approach

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.

Dry Run: n = 1023
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 → true
Insight & Complexity

Time: O(d) | Space: O(1). Digit-by-digit validation is always cleaner than converting to string for single-property checks.

P14 Binary Representation & Bit Shifts Beginner

(A) Print the binary representation of integer n. (B) Double and halve n using bit shifts.

Approach

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).

Dry Run: binary of n = 13
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";
Insight & Complexity

Time: O(1) | Space: O(1). Bit shifts are single CPU instructions — much faster than division/multiplication. Left shift by k = multiply by 2ᵏ.

P15 Leap Year Beginner

Determine if a year is a leap year. Rules: divisible by 4, BUT not by 100, UNLESS also divisible by 400.

The Three-Rule Logic

Test in order: if ÷400 → leap. Else if ÷100 → not leap. Else if ÷4 → leap. Else → not leap. Compress to one boolean expression.

Dry Run: 1900, 2000, 2024
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);
}
Insight & Complexity

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.

P16 Sparse Number Bit Logic

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.

Approach — One Bitwise Check

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 (101) vs n=6 (110)
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;
}
Insight & Complexity

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.

↑ back to top
Chapter 02
Decision Making & Conditions — if/else, Nested Logic, Multi-way Decisions

P17 Greatest of Three Numbers Beginner

Find the greatest among three integers a, b, c.

Inputa=9, b=4, c=9
Output9
Three Approaches

if-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.

Dry Run: a=9, b=4, c=7
a>=b? 9>=4 Yes.  a>=c? 9>=7 Yes.  →  Greatest = 9
int 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";
Insight & Complexity

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.

P18 Vowel or Consonant Beginner

Check if a given character is a vowel (a, e, i, o, u), case-insensitive.

Input'E'
OutputVowel
Dry Run: c = 'E'
tolower('E') = 'e'
"aeiou".find('e') found  →  Vowel
bool isVowel(char c){
    c = tolower(c);
    return c=='a'||c=='e'||c=='i'||c=='o'||c=='u';
}
// Or: string("aeiou").find(tolower(c)) != string::npos
Insight & Complexity

Time: 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.

P19 Case Conversion Beginner

Convert a character or entire string between upper and lower case. Understand the ASCII relationship.

The ASCII Trick

'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.

Dry Run: c = 'a' (ASCII 97)
'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"
Insight & Complexity

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.

P20 Opposite Face of a Dice Beginner

Given a face value on a standard die, find the opposite face. Opposite faces sum to 7: (1↔6), (2↔5), (3↔4).

Inputtop = 3
Output4
Verify all pairs
7 - 1 = 6 ✓    7 - 2 = 5 ✓    7 - 3 = 4 ✓
7 - 4 = 3 ✓    7 - 5 = 2 ✓    7 - 6 = 1 ✓
int oppositeFace(int top){ return 7 - top; }
Insight & Complexity

Time: O(1) | Space: O(1). Pattern recognition: when elements pair to a constant sum, always express the relationship as constant − value.

P21 Replace All 0s with 5 Beginner

Replace every digit 0 in a number with 5. Example: 1020 → 1525.

Approach — Digit Rebuild

Extract each digit. If it's 0, use 5 instead. Rebuild the number using positional arithmetic (multiply by place value).

Dry Run: n = 1020
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 = 1525
int 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;
}
Insight & Complexity

Time: O(d) | Space: O(1). The rebuild pattern (accumulate into result with multiplied place value) is the reverse of digit extraction — master both.

↑ back to top
Chapter 03
Loops & Series — Iteration, Summation & Series Patterns

P22 Sum of First N Natural Numbers Beginner

(A) Sum 1+2+3+…+n. (B) Sum of sums: S(n) = (1) + (1+2) + (1+2+3) + … + (1+…+n).

Use Closed-Form Formulas

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.

Dry Run: n = 4
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;
}
Insight & Complexity

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.

P23 Multiplication Table Beginner

Print the multiplication table for a number n, from 1 to 10.

Inputn = 5
Output5×1=5, 5×2=10, ..., 5×10=50
int n; cin>>n;
for(int i=1; i<=10; i++)
    cout << n << " x " << i << " = " << n*i << "\n";
Insight & Complexity

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.

P24 Fibonacci Series Beginner

Print the first n Fibonacci numbers. Series: 0, 1, 1, 2, 3, 5, 8, 13, … Each term = sum of previous two.

Approach — Two-Variable Rolling Update

Keep only two variables: a (current) and b (next). Each step: print a, then update a=b, b=a+b. No array needed.

Dry Run: n = 7
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 8
void 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";
}
Insight & Complexity

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).

P25 AP and GP Summation Series

(A) Sum of Arithmetic Progression: a, a+d, a+2d, … n terms. (B) Sum of Geometric Progression: a, ar, ar², … n terms.

Closed-Form Formulas

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.

Dry Run: AP a=2, d=3, n=4 → 2,5,8,11
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);
}
Insight & Complexity

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).

P26 Triangular Numbers — 1, 3, 6, 10, 15… Pattern Recognition

Find the n-th triangular number. Series: 1, 3, 6, 10, 15, 21… (each term = previous + n).

Spot the Pattern

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.

Verify n = 5
5×6/2 = 15  ✓   (1+2+3+4+5 = 15)
long long triangular(int n){
    return (long long)n*(n+1)/2;
}
Insight & Complexity

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.

P27 Mean, Median & Running Average Stats

(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.

Running Average — Welford's Method

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.

Running avg: stream = [4, 8, 6]
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";
    }
}
Insight & Complexity

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.

P28 Fascinating Number Number Theory

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.

Approach

Concatenate n, 2n, 3n as strings. Check length is exactly 9. Sort the string and compare to "123456789".

Dry Run: n = 192
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)
Insight & Complexity

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.

↑ back to top