---
id: binary-search
title: "Binary Search on the Answer"
author: Darren Yao
prerequisites: 
 - Silver - Introduction to Sorting
description: You should already be familiar with the concept of binary searching for a number in a sorted array. However, binary search can be extended to binary searching on the answer itself. 
frequency: 3
---

import { Problem } from "../models";

export const metadata = {
  problems: {
    ex: [
      new Problem("CF", "Div 2 C - Maximum Median", "contest/1201/problem/C", "Easy", false, [], ""),
    ],
    general: [
      new Problem("CSES", "Factory Machines", "1620", "Easy", false, []),
      new Problem("Silver", "Moo Buzz", "966", "Intro", false, [], "binary search not required"),
      new Problem("Silver", "Cow Dance Show", "690", "Normal", false, [], "binary search on $K$ and simulate"),
      new Problem("Silver", "Convention", "858", "Normal", false, [], "determine whether $M$ buses suffice if every cow waits at most $T$ minutes, use a greedy strategy (applies to next two problems as well)"),
      new Problem("Silver", "Angry Cows", "594", "Normal", false, [], "check in $O(N)$ how many haybales you can destroy with fixed radius $R$"),
      new Problem("Silver", "Social Distancing", "1038", "Normal", false, [], "check in $O(N+M)$ how many cows you can place with distance $D$"),
      new Problem("Silver", "Loan Repayment", "991", "Hard", false, [], "requires some rather tricky analysis to speed up naive $O(N\log N)$ solution"),
      new Problem("CF", "Edu C: Magic Ship", "problemset/problem/1117/", "Normal", false, ["Binary Search", "Prefix Sums"], ""),
      new Problem("CF", "The Meeting Place Cannot Be Changed", "contest/782/problem/B", "Normal", false, [], ""),
      new Problem("CF", "Preparing for Merge Sort", "contest/847/problem/B", "Normal", false, [], ""),
      new Problem("CF", "Level Generation", "problemset/problem/818/F", "Normal", false, [], ""),
      new Problem("CF", "Packmen", "contest/847/problem/E", "Normal", false, [], ""),
      new Problem("CF", "Office Keys", "problemset/problem/830/A", "Normal", false, [], ""),
    ],
  }
};

## Introduction

When we binary search on the answer, we start with a search space of size $N$ which we know the answer lies in. Then, each iteration of the binary search cuts the search space in half, so the algorithm tests $O(\log N)$ values. This is efficient and much better than testing each possible value in the search space.

Let's say we have a function `check(x)` that returns true if the answer of $x$ is possible, and false otherwise. Usually, in such problems, we'll want to find the maximum or minimum value of $x$ such that `check(x)` is true. Similarly to how binary search on an array only works on a sorted array, binary search on the answer only works if the answer function is [monotonic](https://en.wikipedia.org/wiki/Monotonic_function), meaning that it is always non-decreasing or always non-increasing. 

In particular, if we want to find the maximum `x` such that `check(x)` is true, then we can binary search if `check(x)` satisfies both of the following conditions:

 - If `check(x)` is `true`, then `check(y)` is true for all $y \leq x$.
 - If `check(x)` is `false`, then `check(y)` is false for all $y \geq x$.

In other words, we want to reduce the search space to something of the following form, using a check function as we described above.

<center>true true true true true false false false false</center>

We want to find the point at which `true` becomes `false`.

Below, we present two algorithms for binary search. The first implementation may be more intuitive, because it's closer to the binary search most students learned, while the second implementation is shorter.

(pseudocode)

If instead we're looking for the minimum `x` that satisfies some condition, then we can binary search if `check(x)` satisfies both of the following conditions:

 - If `check(x)` is true, then `check(y)` is true for all $y \geq x$.
 - If `check(x)` is false, then `check(y)` is false for all $y \leq x$.

The binary search function for this is very similar. Find the maximum value of $x$ such that `check(x)` is false with the algorithm above, and return $x+1$.

## Example: Maximum Median

<problems-list problems={metadata.problems.ex} />

**Statement:** Given an array $\texttt{arr}$ of $n$ integers, where $n$ is odd, we can perform the following operation on it $k$ times: take any element of the array and increase it by $1$. We want to make the median of the array as large as possible after $k$ operations.

**Constraints:** $1 \leq n \leq 2 \cdot 10^5, 1 \leq k \leq 10^9$ and $n$ is odd.

**Solution:** We first sort the array in ascending order. Then, we binary search for the maximum possible median. We know that the number of operations required to raise the median to $x$ increases monotonically as $x$ increases, so we can use binary search. For a given median value $x$, the number of operations required to raise the median to $x$ is

$$
\sum_{i=(n+1)/2}^{n} \max(0, x - \texttt{arr}[i]).
$$

If this value is less than or equal to $k$, then $x$ can be the median, so our check function returns `true`. Otherwise, $x$ cannot be the median, so our check function returns `false`.

The solution codes use the second implementation of binary search.

Java:

```java
static int n;
static long k;
static long[] arr;
public static void main(String[] args) {
    
    n = r.nextInt(); k = r.nextLong();
    arr = new long[n];
    for(int i = 0; i < n; i++){
        arr[i] = r.nextLong();
    }
    Arrays.sort(arr);

    pw.println(search());
    pw.close();
}

// binary searches for the correct answer
static long search(){
    long pos = 0; long max = (long)2E9;
    for(long a = max; a >= 1; a /= 2){
        while(check(pos+a)) pos += a;
    }
    return pos;
}

// checks whether the number of given operations is sufficient
// to raise the median of the array to x
static boolean check(long x){
    long operationsNeeded = 0;
    for(int i = (n-1)/2; i < n; i++){
        operationsNeeded += Math.max(0, x-arr[i]);
    }
    if(operationsNeeded <= k){ return true; }
    else{ return false; }
}
```

Note that I used the `Arrays.sort()` function, which uses quicksort on primitive data types such as `long`s. This is fine for USACO, but in other contests, such as CodeForces, it may time out on test cases specifically engineered to trigger worst-case $O(n^2)$ behavior in quicksort. In such contests, you should declare the underlying array as an array of objects, for example `Long` instead of `long`. This forces the `Arrays.sort()` function to use mergesort, which is always O(n log n).

C++:

```cpp
int n;
long long k;
vector<long long> v;

// checks whether the number of given operations is sufficient
// to raise the median of the array to x
bool check(long long x){
    long long operationsNeeded = 0;
    for(int i = (n-1)/2; i < n; i++){
        operationsNeeded += max(0, x-v[i]);
    }
    if(operationsNeeded <= k) return true; 
    else return false; 
}

// binary searches for the correct answer
long long search(){
    long long pos = 0; long long max = 2E9;
    for(long long a = max; a >= 1; a /= 2){
        while(check(pos+a)) pos += a;
    }
    return pos;
}

int main() {
    cin >> n >> k;
    for(int i = 0; i < n; i++){
        int t;
        cin >> t;
        v.push_back(t);
    }
    sort(v.begin(), v.end());

    cout << search() << '\n';
}
```

### Problems

<problems-list problems={metadata.problems.general} />
