Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reorg Silver sorting and searching modules, move some modules to Gold #5176

Merged
merged 12 commits into from
Mar 6, 2025
317 changes: 5 additions & 312 deletions content/3_Silver/Binary_Search.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ redirects:
title: 'Binary Search'
author: Darren Yao, Abutalib Namazov, Andrew Wang, Qi Wang, Dustin Miao
prerequisites:
- greedy-sorting
- binary-search-sorted-array
description:
'Binary searching on arbitrary monotonic functions and built-in functions for
binary search.'
'Binary searching on arbitrary monotonic functions.'
frequency: 3
---

<!-- process numbers from left to right, keep track of the last element of each block and use binary search of lower_bound to find which block the current numbers belongs -->

## Introduction

<Resources>
Expand Down Expand Up @@ -57,13 +54,14 @@ frequency: 3

<YouTube id="GU7DpgHINWQ" />

When we binary search on an answer, we start with a search space of size $N$
When we binary search, 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 $\mathcal{O}(\log N)$ values.
This is efficient and much better than testing each possible value in the search
space.

## Binary Searching on Monotonic Functions

## Binary Search on Monotonic Functions

Let's say we have a boolean function `f(x)`. Usually, in such problems, we want
to find the maximum or minimum value of $x$ such that `f(x)` is true. Similarly
Expand Down Expand Up @@ -812,311 +810,6 @@ The first version of `firstTrue` won't work if `hi-lo` initially exceeds
`INT_MAX` at any point during execution. If this is an issue, use `long long`s
instead of `int`s.

## Library Functions For Binary Search

<LanguageSection>

<CPPSection>

<Resources>
<Resource
source="CPP"
url="http://www.cplusplus.com/reference/algorithm/lower_bound/"
title="lower_bound, upper_bound"
starred
>
with examples
</Resource>
</Resources>

</CPPSection>

<JavaSection>

<Resources>
<Resource
source="JAVA"
url="https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#binarySearch(int[],%20int)"
title="Arrays.binarySearch"
/>
<Resource
source="JAVA"
url="https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#binarySearch(java.util.List,%20T)"
title="Collections.binarySearch"
/>
</Resources>

</JavaSection>

<PySection>

<Resources>
<Resource
source="Python"
url="https://docs.python.org/3/library/bisect.html"
title="Array bisection algorithm"
/>
<Resource
source="GFG"
url="https://www.geeksforgeeks.org/binary-search-bisect-in-python/"
title="Binary Search (bisect) in Python"
/>
</Resources>

</PySection>

</LanguageSection>

### Example - Counting Haybales

<FocusProblem problem="count" />

As each of the points are in the range $0 \ldots 1\,000\,000\,000$, storing
locations of haybales in a boolean array and then taking prefix sums of that
would take too much time and memory.

Instead, let's place all of the locations of the haybales into a list and sort
it. Now we can use binary search to count the number of cows in any range
$[A,B]$ in $\mathcal{O}(\log N)$ time.

### With Built-in Function

<LanguageSection>

<CPPSection>

We can use the the built-in
[`lower_bound`](https://en.cppreference.com/w/cpp/algorithm/lower_bound) and
[`upper_bound`](https://en.cppreference.com/w/cpp/algorithm/upper_bound)
functions.

```cpp
#include <bits/stdc++.h>
using namespace std;

void setIO(string name = "") { // name is nonempty for USACO file I/O
ios_base::sync_with_stdio(0);
cin.tie(0); // see Fast Input & Output
// alternatively, cin.tie(0)->sync_with_stdio(0);
if (!name.empty()) {
freopen((name + ".in").c_str(), "r", stdin); // see Input & Output
freopen((name + ".out").c_str(), "w", stdout);
}
}

int main() {
setIO("haybales");
int bale_num;
int query_num;
cin >> bale_num >> query_num;
vector<int> bales(bale_num);
for (int i = 0; i < bale_num; i++) { cin >> bales[i]; }

sort(begin(bales), end(bales));
for (int i = 0; i < query_num; i++) {
int q_start;
int q_end;
cin >> q_start >> q_end;
cout << upper_bound(begin(bales), end(bales), q_end) -
lower_bound(begin(bales), end(bales), q_start)
<< "\n";
}
}
```

</CPPSection>
<JavaSection>

We can use the builtin
[`Arrays.binarySearch`](<https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#binarySearch(int[],%20int)>)
function.

```java
import java.io.*;
import java.util.*;

public class Haybales {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(new File("haybales.in")));
PrintWriter out = new PrintWriter("haybales.out");
StringTokenizer st = new StringTokenizer(br.readLine());

int baleNum = Integer.parseInt(st.nextToken());
int queryNum = Integer.parseInt(st.nextToken());
int[] bales = new int[baleNum];
st = new StringTokenizer(br.readLine());
for (int i = 0; i < baleNum; i++) {
bales[i] = Integer.parseInt(st.nextToken());
}

Arrays.sort(bales);
for (int i = 0; i < queryNum; i++) {
st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());

// Get the left-est bale that's still in the interval
int bi = Arrays.binarySearch(bales, start);
if (bi < 0) { bi = Math.abs(bi + 1); }

// And also get the right-est bale that's still in the interval
int ti = Arrays.binarySearch(bales, end);
if (ti < 0) { ti = Math.abs(ti + 2); }
out.println(ti - bi + 1);
}
out.close();
}
}
```

</JavaSection>
<PySection>

We can use the builtin
[`bisect.bisect`](https://docs.python.org/3/library/bisect.html#bisect.bisect)
function.

```py
from bisect import bisect

inp = open("haybales.in", "r")
out = open("haybales.out", "w")

bale_num, query_num = map(int, inp.readline().split())
bales = sorted(list(map(int, inp.readline().split())))
for _ in range(query_num):
start, end = map(int, inp.readline().split())
print(bisect(bales, end) - bisect(bales, start - 1), file=out)
```

</PySection>
</LanguageSection>

### Without Using Built-in Functions

<LanguageSection>
<CPPSection>

```cpp
#include <bits/stdc++.h>
using namespace std;

void setIO(string name = "") { // name is nonempty for USACO file I/O
ios_base::sync_with_stdio(0);
cin.tie(0); // see Fast Input & Output
// alternatively, cin.tie(0)->sync_with_stdio(0);
if (!name.empty()) {
freopen((name + ".in").c_str(), "r", stdin); // see Input & Output
freopen((name + ".out").c_str(), "w", stdout);
}
}

int main() {
setIO("haybales");
int bale_num;
int query_num;
cin >> bale_num >> query_num;
vector<int> bales(bale_num);
for (int &i : bales) { cin >> i; }

sort(begin(bales), end(bales));
// Returns the number of elements that are at most x
auto atMost = [&](int x) {
int lo = 0;
int hi = bales.size();
while (lo < hi) {
int mid = (lo + hi) / 2;
if (bales[mid] <= x) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
};

for (int i = 0; i < query_num; ++i) {
int q_start;
int q_end;
cin >> q_start >> q_end;
cout << atMost(q_end) - atMost(q_start - 1) << "\n";
}
}
```

</CPPSection>
<JavaSection>

```java
import java.io.*;
import java.util.*;

public class Haybales {
static int[] bales;
public static void main(String[] args) throws IOException {
Kattio io = new Kattio("haybales");
int baleNum = io.nextInt();
int queryNum = io.nextInt();
bales = new int[baleNum];
for (int i = 0; i < baleNum; i++) { bales[i] = io.nextInt(); }
Arrays.sort(bales);

for (int i = 0; i < queryNum; ++i) {
int start = io.nextInt();
int end = io.nextInt();
io.println(atMost(end) - atMost(start - 1));
}
io.close();
}

// Returns the number of elements that are at most x
public static int atMost(int x) {
int lo = 0;
int hi = bales.length;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (bales[mid] <= x) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}

// CodeSnip{Kattio}
}
```

</JavaSection>
<PySection>

```py
def at_most(x: int) -> int:
lo = 0
hi = len(bales)
while lo < hi:
mid = (lo + hi) // 2
if bales[mid] <= x:
lo = mid + 1
else:
hi = mid
return lo


inp = open("haybales.in", "r")
out = open("haybales.out", "w")

bale_num, query_num = map(int, inp.readline().split())
bales = sorted(list(map(int, inp.readline().split())))
for _ in range(query_num):
start, end = map(int, inp.readline().split())
print(at_most(end) - at_most(start - 1), file=out)
```

</PySection>

</LanguageSection>

## Problems

Expand Down
38 changes: 12 additions & 26 deletions content/3_Silver/Binary_Search.problems.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,33 +116,7 @@
}
}
],
"count": [
{
"uniqueId": "usaco-666",
"name": "Counting Haybales",
"url": "http://www.usaco.org/index.php?page=viewproblem2&cpid=666",
"source": "Silver",
"difficulty": "Easy",
"isStarred": false,
"tags": ["Binary Search"],
"solutionMetadata": {
"kind": "internal"
}
}
],
"general": [
{
"uniqueId": "cf-702C",
"name": "Cellular Network",
"url": "https://codeforces.com/contest/702/problem/C",
"source": "CF",
"difficulty": "Easy",
"isStarred": false,
"tags": ["Binary Search"],
"solutionMetadata": {
"kind": "internal"
}
},
{
"uniqueId": "cses-1620",
"name": "Factory Machines",
Expand Down Expand Up @@ -179,6 +153,18 @@
"kind": "internal"
}
},
{
"uniqueId": "cf-862E",
"name": "Mahmoud & Ehab & Function",
"url": "https://codeforces.com/contest/862/problem/E",
"source": "CF",
"difficulty": "Normal",
"isStarred": false,
"tags": ["Binary Search"],
"solutionMetadata": {
"kind": "internal"
}
},
{
"uniqueId": "cc-TRPTSTIC",
"name": "TripTastic",
Expand Down
Loading