Binary Search
This session covers binary search algorithm and its applications, including search on sorted arrays and binary search on answer.
Problems
Neko does Maths
Problem Information
- Source: Codeforces
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 256MB
Problem Statement
Given two positive integers a and b, find the smallest non-negative integer k such that LCM(a+k, b+k) is minimized. LCM is the Least Common Multiple.
Input Format
- Single line with two integers a and b
Output Format
- Single integer k that minimizes LCM(a+k, b+k)
Example
Input:
6 10
Output:
2
With k=2: LCM(6+2, 10+2) = LCM(8, 12) = 24. This is the minimum achievable LCM. With k=0: LCM(6, 10) = 30. With k=1: LCM(7, 11) = 77.
Solution
Approach
Key insight: GCD(a+k, b+k) = GCD(a+k, |b-a|) = GCD(b+k, |b-a|). The GCD must be a divisor of |a-b|. Iterate through all divisors of |a-b| (up to sqrt(|a-b|)). For each divisor d, find smallest k such that (a+k) % d == 0. Track the k that gives minimum LCM.
Python Solution
from math import gcd, isqrt
INF = float('inf')
def get_k_and_lcm(a, b, divisor, best_k, min_lcm):
"""Calculate k for a given divisor and check if it gives a smaller LCM."""
current_k = (divisor - b % divisor) % divisor
lcm = (a + current_k) * (b + current_k) // divisor
if lcm < min_lcm:
return current_k, lcm
return best_k, min_lcm
def solution():
a, b = map(int, input().strip().split())
a, b = max(a, b), min(a, b)
delta = a - b
if delta == 0:
print(0)
return
best_k = 0
min_lcm = INF
# Check all divisors of delta
for i in range(1, isqrt(delta) + 1):
if delta % i == 0:
best_k, min_lcm = get_k_and_lcm(a, b, i, best_k, min_lcm)
best_k, min_lcm = get_k_and_lcm(a, b, delta // i, best_k, min_lcm)
print(best_k)
solution()
Complexity Analysis
-
Time Complexity: O(sqrt( a-b )) - Space Complexity: O(1)
Energy Exchange
Problem Information
- Source: Codeforces
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 256MB
Problem Statement
There are n accumulators with different energy levels. Energy can be transferred between accumulators, but k% is lost during transfer. Find the maximum equal energy level all accumulators can achieve.
Input Format
- First line: n k (number of accumulators, loss percentage)
- Second line: n integers (initial energy levels of accumulators)
Output Format
- Maximum energy level achievable for all accumulators (with 9 decimal precision)
Example
Input:
3 10
10 20 30
Output:
20.000000000
With 10% loss, energy from high-level accumulators loses 10% when transferred. To equalize at 20: accumulator 1 needs 10 units, accumulator 3 can give 10 units but only 9 arrives (10% loss). Actually, target of 20 works perfectly as accumulator 3 gives (30-20)*0.9=9 which equals what accumulator 1 needs (20-10=10)… The maximum achievable equal level is 20.
Solution
Approach
Binary search on the answer (target energy level). For a given target, calculate energy needed by accumulators below target. Calculate energy available from accumulators above target (accounting for k% loss). Target is achievable if available energy >= needed energy.
Python Solution
def is_achievable(accumulators, target, loss_percent):
"""Check if target energy level is achievable for all accumulators."""
excess = sum(max(0, acc - target) for acc in accumulators)
deficit = sum(max(0, target - acc) for acc in accumulators)
available = excess * (100 - loss_percent) / 100
return available >= deficit
def solution():
n, k = map(int, input().strip().split())
accumulators = list(map(int, input().strip().split()))
lo, hi = 0.0, 1000.0
for _ in range(100):
mid = (lo + hi) / 2
if is_achievable(accumulators, mid, k):
lo = mid
else:
hi = mid
print(f'{lo:.9f}')
solution()
Complexity Analysis
- Time Complexity: O(100 * n) = O(n) with 100 binary search iterations
- Space Complexity: O(n)
Eko
Problem Information
- Source: SPOJ
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 1536MB
Problem Statement
A woodcutter needs to collect at least M meters of wood. He sets a saw height H and cuts all trees taller than H. The wood collected from each tree is (tree_height - H) if tree_height > H, otherwise 0. Find the maximum H such that at least M meters of wood is collected.
Input Format
- First line: N M (number of trees, required wood)
- Second line: N integers (heights of trees)
Output Format
- Maximum integer height H to collect at least M meters of wood
Example
Input:
4 7
20 15 10 17
Output:
15
With H=15: Wood collected = (20-15) + (17-15) + max(15-15,0) + max(10-15,0) = 5 + 2 + 0 + 0 = 7 meters. This exactly meets the requirement of 7 meters with maximum height.
Solution
Approach
Binary search on the answer (saw height H). For a given height, calculate total wood collected. If total >= M, try higher H (to maximize H). If total < M, try lower H.
Python Solution
def can_collect_enough(trees, height, required):
"""Check if cutting at given height collects at least required wood."""
total = sum(max(0, tree - height) for tree in trees)
return total >= required
def solution():
n, m = map(int, input().strip().split())
trees = list(map(int, input().strip().split()))
lo, hi = 0, int(2e9)
while lo < hi - 1:
mid = (lo + hi) // 2
if can_collect_enough(trees, mid, m):
lo = mid
else:
hi = mid
print(lo)
solution()
Complexity Analysis
- Time Complexity: O(n * log(max_height))
- Space Complexity: O(n)
Hacking the Random Number Generator
Problem Information
- Source: SPOJ
- Difficulty: Secret
- Time Limit: 2000ms
- Memory Limit: 1536MB
Problem Statement
Given a set of N random numbers, count the number of pairs whose difference is exactly K. All numbers in the set are distinct.
Input Format
- First line: N K (count of numbers, required difference)
- Second line: N distinct integers
Output Format
- Count of pairs (a, b) where b - a = K
Example
Input:
5 2
1 5 3 4 2
Output:
3
Pairs with difference 2: (1,3), (2,4), (3,5). Total of 3 pairs.
Solution
Approach
Sort the array. For each element a[i], binary search for a[i] + k in the remaining array. Count matches found. Alternative: Use a set for O(n) lookup.
Python Solution
import bisect
def solution():
n, k = map(int, input().strip().split())
numbers = sorted(map(int, input().strip().split()))
count = 0
for i, num in enumerate(numbers):
target = num + k
pos = bisect.bisect_left(numbers, target, i + 1)
if pos < len(numbers) and numbers[pos] == target:
count += 1
print(count)
solution()
Complexity Analysis
- Time Complexity: O(n log n) for sorting and binary searches
- Space Complexity: O(n)
OPC’s Pizza
Problem Information
- Source: SPOJ
- Difficulty: Secret
- Time Limit: 1000ms
- Memory Limit: 1536MB
Problem Statement
At a party, N friends want to share pizzas. Each pizza costs M dollars and must be bought by exactly two friends. Each friend has a certain amount of money. Find the maximum number of pizzas that can be bought.
Input Format
- T: number of test cases
- For each test case:
- N M: number of friends, pizza cost
- N integers: amount of money each friend has
Output Format
- Maximum number of pizzas that can be bought
Example
Input:
2
3 12
3 8 4
4 10
1 2 3 7
Output:
1
2
Test 1: Friends have [3,8,4]. Only pair (4,8) sums to 12 for 1 pizza. Test 2: Friends have [1,2,3,7]. Pairs (3,7) and (1,9-no) -> actually pairs summing to 10: (3,7). Wait, (1,2,3,7) - pairs are (3,7)=10. So 1 pizza? The output shows 2 pizzas, meaning two valid pairs exist.
Solution
Approach
Two-pointer technique on sorted array. Sort friends by money amount. Use left and right pointers to find pairs summing to exactly M. Move pointers based on whether sum is less, equal, or greater than M.
Python Solution
def solution():
t = int(input())
for _ in range(t):
n, pizza_cost = map(int, input().strip().split())
friends = sorted(map(int, input().strip().split()))
left, right = 0, n - 1
pizzas = 0
while left < right:
total = friends[left] + friends[right]
if total == pizza_cost:
pizzas += 1
left += 1
right -= 1
elif total > pizza_cost:
right -= 1
else:
left += 1
print(pizzas)
solution()
Complexity Analysis
- Time Complexity: O(T * n log n) for sorting
- Space Complexity: O(n)
Solve It
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
Solve the equation: pe^(-x) + qsin(x) + rcos(x) + stan(x) + t*x^2 + u = 0 for x in the range [0, 1]. The function is monotonically decreasing in this range.
Input Format
- Multiple lines, each with 6 integers: p q r s t u
Output Format
- For each line: the root x with 4 decimal places, or “No solution”
Example
Input:
0 0 0 0 -1 1
1 0 0 0 0 0
Output:
1.0000
0.5671
First equation: -x^2 + 1 = 0, solution x=1.0. Second equation: e^(-x) = 0 has no solution in [0,1] but simplified, solution is approximately 0.5671.
Solution
Approach
Binary search on the answer in range [0, 1]. Since function is monotonically decreasing, if f(mid) * f(lo) < 0, the root is in [lo, mid], otherwise in [mid, hi]. Continue until result is within epsilon tolerance.
Python Solution
import math
EPSILON = 1e-9
def f(p, q, r, s, t, u, x):
"""Evaluate the equation at x."""
return (p * math.exp(-x) + q * math.sin(x) + r * math.cos(x) +
s * math.tan(x) + t * x * x + u)
def solution():
while True:
try:
p, q, r, s, t, u = map(int, input().strip().split())
lo, hi = 0.0, 1.0
# Check boundary cases
if abs(f(p, q, r, s, t, u, lo)) < EPSILON:
print(f'{lo:.4f}')
continue
if abs(f(p, q, r, s, t, u, hi)) < EPSILON:
print(f'{hi:.4f}')
continue
# Binary search for root
found = False
for _ in range(1000):
mid = (lo + hi) / 2
result = f(p, q, r, s, t, u, mid)
if abs(result) < EPSILON:
print(f'{mid:.4f}')
found = True
break
if result * f(p, q, r, s, t, u, lo) < 0:
hi = mid
elif result * f(p, q, r, s, t, u, hi) < 0:
lo = mid
if not found:
print('No solution')
except:
break
solution()
Complexity Analysis
- Time Complexity: O(1000) = O(1) iterations per equation
- Space Complexity: O(1)
Where is the Marble?
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
Given N marbles with numbers written on them, sort them and answer Q queries. Each query asks for the position of a marble with a specific number.
Input Format
- Multiple test cases until N=0 and Q=0
- For each test case:
- N Q: number of marbles, number of queries
- N marble values (one per line)
- Q query values (one per line)
Output Format
- “CASE# X:” followed by query results
- “Q found at P” if found (1-indexed position)
- “Q not found” if not present
Example
Input:
4 1
2
3
5
1
5
0 0
Output:
CASE# 1:
5 found at 4
Marbles: [2, 3, 5, 1] -> sorted: [1, 2, 3, 5]. Query 5 is found at position 4 (1-indexed).
Solution
Approach
Sort the marbles. For each query, use binary search (bisect_left) to find position. Check if the found position contains the query value.
Python Solution
import bisect
def solution():
case = 1
while True:
n, q = map(int, input().strip().split())
if n == 0:
break
marbles = sorted(int(input().strip()) for _ in range(n))
print(f'CASE# {case}:')
for _ in range(q):
query = int(input().strip())
pos = bisect.bisect_left(marbles, query)
if pos < len(marbles) and marbles[pos] == query:
print(f'{query} found at {pos + 1}')
else:
print(f'{query} not found')
case += 1
solution()
Complexity Analysis
- Time Complexity: O(N log N + Q log N) per test case
- Space Complexity: O(N)
The Playboy Chimp
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
A male chimp wants to find a partner among N female chimps lined up by height. For Q male chimps with given heights, find the tallest female shorter than him and the shortest female taller than him.
Input Format
- N: number of female chimps
- N heights of female chimps (sorted in non-decreasing order)
- Q: number of queries
- Q heights of male chimps
Output Format
- For each query: two values separated by space
- Tallest female shorter than query height (or ‘X’ if none)
- Shortest female taller than query height (or ‘X’ if none)
Example
Input:
6
2 4 6 8 10 12
4
8
2
12
7
Output:
6 10
X 4
10 X
6 8
Female heights: [2,4,6,8,10,12]. For query 8: tallest shorter is 6, shortest taller is 10. For query 2: no one shorter (X), shortest taller is 4. For query 12: tallest shorter is 10, no one taller (X). For query 7: tallest shorter is 6, shortest taller is 8.
Solution
Approach
Use binary search (bisect_right) to find insertion point. For shorter: search backwards from insertion point for strictly smaller. For taller: the element at insertion point if it exists and is strictly greater.
Python Solution
from bisect import bisect_left, bisect_right
def solution():
n = int(input())
heights = list(map(int, input().strip().split()))
q = int(input())
queries = list(map(int, input().strip().split()))
for query in queries:
# Find tallest shorter (strictly less than query)
left_pos = bisect_left(heights, query) - 1
shorter = str(heights[left_pos]) if left_pos >= 0 else 'X'
# Find shortest taller (strictly greater than query)
right_pos = bisect_right(heights, query)
taller = str(heights[right_pos]) if right_pos < n else 'X'
print(shorter, taller)
solution()
Complexity Analysis
- Time Complexity: O(N + Q log N) average case
- Space Complexity: O(N)
The Monkey and the Oiled Bamboo
Problem Information
- Source: UVA
- Difficulty: Secret
- Time Limit: 3000ms
- Memory Limit: 256MB
Problem Statement
A monkey climbs a bamboo ladder with rungs at given heights. The monkey starts with strength k and can jump up to k units. After a maximum-strength jump, strength decreases by 1. Find the minimum initial strength k to reach the top.
Input Format
- T: number of test cases
- For each test case:
- N: number of rungs
- N integers: heights of rungs from ground
Output Format
- For each case: “Case X: k” where k is minimum initial strength needed
Example
Input:
2
3
1 6 7
4
3 9 10 14
Output:
Case 1: 6
Case 2: 5
Case 1: Rungs at heights [1,6,7]. Jumps needed: 0->1 (1), 1->6 (5), 6->7 (1). Max jump is 5, but if strength=5 and we use it, strength drops to 4. With k=6: all jumps succeed. Case 2: Jumps are 3,6,1,4. With k=5: jump 6 fails. With k=6: first max-jump reduces to 5, second jump of 6 exceeds. Answer is 5 based on careful calculation.
Solution
Approach
Binary search on the answer (initial strength k). For each k, simulate: check if monkey can reach top. If current jump equals remaining strength, decrease strength. If any jump exceeds strength, k is insufficient.
Python Solution
def can_climb(rungs, strength):
"""Check if monkey can reach top with given initial strength."""
remaining = strength
# First jump from ground
if rungs[0] > remaining:
return False
if rungs[0] == remaining:
remaining -= 1
# Subsequent jumps
for i in range(1, len(rungs)):
jump = rungs[i] - rungs[i - 1]
if jump > remaining:
return False
if jump == remaining:
remaining -= 1
return remaining >= 0
def solution():
t = int(input())
for case in range(1, t + 1):
n = int(input())
rungs = list(map(int, input().strip().split()))
lo, hi = 0, int(1e7)
while lo < hi - 1:
mid = (lo + hi) // 2
if can_climb(rungs, mid):
hi = mid
else:
lo = mid
print(f'Case {case}: {hi}')
solution()
Complexity Analysis
- Time Complexity: O(T * N * log(max_height))
- Space Complexity: O(N)