diff --git a/oficina-busca-binaria/binary-search.md b/oficina-busca-binaria/binary-search.md
new file mode 100644
index 0000000000000000000000000000000000000000..9e13762cfa97df66746c508b64f5a9a4d4c22e96
--- /dev/null
+++ b/oficina-busca-binaria/binary-search.md
@@ -0,0 +1,132 @@
+# Busca Binária e suas diferentes implementações
+
+Implementação com intervalo [0, n-1]
+```cpp
+int lo = 0;
+int hi = n - 1;
+while (l <= r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] == target) {
+        return mid;
+    } else if (a[mid] > target) {
+        hi = mid - 1;
+    } else {
+        lo = mid + 1;
+    }
+}
+
+return -1;
+```
+
+Implementação com intervalo [0, n)
+```cpp
+int lo = 0;
+int hi = n;
+while (l < r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] == target) {
+        return mid;
+    } else if (a[mid] > target) {
+        hi = mid;
+    } else {
+        lo = mid + 1;
+    }
+}
+
+return -1;
+```
+
+Implementação com intervalo [0, n-1] e verificação no fim. Requer na média uma
+iteração a mais.
+```cpp
+int lo = 0;
+int hi = n - 1;
+while (l < r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] > target) {
+        hi = mid + 1;
+    } else {
+        lo = mid;
+    }
+}
+
+if (a[lo] == target) {
+    return lo;
+} else {
+    return -1;
+}
+```
+
+Implementação com intervalo [0, n) para achar o elemento mais a esquerda.
+```cpp
+int lo = 0;
+int hi = n;
+while (l < r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] >= target) {
+        hi = mid;
+    } else {
+        lo = mid + 1;
+    }
+}
+
+return lo;
+```
+
+Implementação com intervalo [0, n-1] para achar o elemento mais a esquerda.
+```cpp
+int lo = 0;
+int hi = n - 1;
+while (l <= r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] >= target) {
+        hi = mid - 1;
+    } else {
+        lo = mid + 1;
+    }
+}
+
+return lo;
+```
+
+Implementação com intervalo [0, n-1] para achar o elemento mais a esquerda. Errichto.
+```cpp
+int lo = 0;
+int hi = n - 1;
+int ans = -1;
+while (l <= r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] >= target) {
+        ans = mid;
+        hi = mid - 1;
+    } else {
+        lo = mid + 1;
+    }
+}
+
+return ans;
+```
+
+Implementação com intervalo [0, n) para achar o elemento mais a direita.
+```cpp
+int lo = 0;
+int hi = n;
+while (l < r) {
+    int mid = lo + (hi - lo) / 2;
+    if (a[mid] > target) {
+        hi = mid;
+    } else {
+        lo = mid + 1;
+    }
+}
+
+return hi - 1;
+```
+
+# Tipos de busca binária
+* Achar elemento exato
+* Achar o primeiro elemento com alguma propriedade
+* Achar o último elemento com alguma propriedade
+
+# Como formular um problema de busca binária
+Pensar em um array particionado em verdadeiro e falso.
diff --git a/oficina-busca-binaria/main.cpp b/oficina-busca-binaria/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..440ec355ceb59218740b14e3d6c94a6cffbc38c3
--- /dev/null
+++ b/oficina-busca-binaria/main.cpp
@@ -0,0 +1,595 @@
+#include <bits/stdc++.h>
+using namespace std;
+
+int comp;
+
+// Buscas
+int busca_intervalo_0_n_1 (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] == target) {
+      return mid;
+    } else if (a[mid] > target) {
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return -1;
+}
+
+int busca_intervalo_0_n (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n;
+  while (lo < hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] == target) {
+      return mid;
+    } else if (a[mid] > target) {
+      hi = mid;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return -1;
+}
+
+int busca_intervalo_0_n_end (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n;
+  while (lo < hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  if (n > 0) comp++;
+  return n > 0 && a[lo] == target ? lo : -1;
+}
+
+int busca_intervalo__1_n (vector<int> a, int n, int target) {
+  int lo = -1;
+  int hi = n;
+  while (lo < hi - 1) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] == target) {
+      return mid;
+    } else if (a[mid] > target) {
+      hi = mid;
+    } else {
+      lo = mid;
+    }
+  }
+
+  return -1;
+}
+
+int busca_intervalo_0_n_1_ceil_end (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo < hi) {
+    int mid = lo + (hi - lo + 1) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid - 1;
+    } else {
+      lo = mid;
+    }
+  }
+
+  if (n > 0) comp++;
+  return n > 0 && a[lo] == target ? lo : -1;
+}
+
+int esquerda_intervalo_0_n_lo (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n;
+  while (lo < hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return lo;
+}
+
+int esquerda_intervalo_0_n_hi (vector<int> a, int n, int target) {
+  int lo = 0; int hi = n;
+  while (lo < hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return hi;
+}
+
+int esquerda_intervalo__1_n_lo (vector<int> a, int n, int target) {
+  int lo = -1;
+  int hi = n;
+  while (lo < hi - 1) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid;
+    } else {
+      lo = mid;
+    }
+  }
+
+  return lo+1;
+}
+
+int esquerda_intervalo__1_n_hi (vector<int> a, int n, int target) {
+  int lo = -1;
+  int hi = n;
+  while (lo < hi - 1) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid;
+    } else {
+      lo = mid;
+    }
+  }
+
+  return hi;
+}
+
+int esquerda_intervalo_0_n_1_ceil_lo (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo < hi) {
+    int mid = lo + (hi - lo + 1) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid - 1;
+    } else {
+      lo = mid;
+    }
+  }
+
+  comp++;
+  return a[lo] >= target ? lo : lo + 1;
+}
+
+int esquerda_intervalo_0_n_1_lo (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return lo;
+}
+
+int esquerda_intervalo_0_n_1_ceil_hi (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo < hi) {
+    int mid = lo + (hi - lo + 1) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid - 1;
+    } else {
+      lo = mid;
+    }
+  }
+
+  return a[hi] >= target ? hi : hi + 1;
+}
+
+int esquerda_intervalo_0_n_1_hi (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return hi + 1;
+}
+
+int esquerda_intervalo_0_n_1_aux (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  int ans = n;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] >= target) {
+      ans = mid;
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return ans;
+}
+
+int esquerda_lower_bound (vector<int> a, int n, int target) {
+  return lower_bound(a.begin(), a.begin() + n, target) - a.begin();
+}
+
+int direita_intervalo_0_n_lo (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n;
+  while (lo < hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return lo;
+}
+
+int direita_intervalo_0_n_hi (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n;
+  while (lo < hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return hi;
+}
+
+int direita_intervalo__1_n_lo (vector<int> a, int n, int target) {
+  int lo = -1;
+  int hi = n;
+  while (lo < hi - 1) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid;
+    } else {
+      lo = mid;
+    }
+  }
+
+  return lo + 1;
+}
+
+int direita_intervalo__1_n_hi (vector<int> a, int n, int target) {
+  int lo = -1;
+  int hi = n;
+  while (lo < hi - 1) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid;
+    } else {
+      lo = mid;
+    }
+  }
+
+  return lo + 1;
+}
+
+int direita_intervalo_0_n_1_lo (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return lo;
+}
+
+int direita_intervalo_0_n_1_ceil_lo (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo < hi) {
+    int mid = lo + (hi - lo + 1) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid - 1;
+    } else {
+      lo = mid;
+    }
+  }
+
+  comp++;
+  return a[lo] <= target ? lo + 1 : lo;
+}
+
+int direita_intervalo_0_n_1_ceil_hi (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo < hi) {
+    int mid = (lo + hi + 1) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid - 1;
+    } else {
+      lo = mid;
+    }
+  }
+
+  comp++;
+  return a[hi] <= target ? hi + 1 : hi;
+}
+
+int direita_intervalo_0_n_1_hi (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return hi + 1;
+}
+
+int direita_intervalo_0_n_1_aux (vector<int> a, int n, int target) {
+  int lo = 0;
+  int hi = n - 1;
+  int ans = n;
+  while (lo <= hi) {
+    int mid = lo + (hi - lo) / 2;
+    comp++;
+    if (a[mid] > target) {
+      ans = mid;
+      hi = mid - 1;
+    } else {
+      lo = mid + 1;
+    }
+  }
+
+  return ans;
+}
+
+int direita_upper_bound (vector<int> a, int n, int target) {
+  return upper_bound(a.begin(), a.begin() + n, target) - a.begin();
+}
+
+typedef function<int(vector<int>, int, int)> binary_search_function;
+typedef function<string(binary_search_function)> test_binary_search_function;
+
+string not_found_if_array_is_empty(binary_search_function impl) {
+  return impl({}, 0, 1) != -1 ? "not -1" : "";
+}
+
+string found_if_in_odd_array(binary_search_function impl) {
+  vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16 };
+  for (int i = 0; i < 9; i++) if (impl(a, 9, a[i]) == -1) { return "false"; }
+  return "";
+}
+
+string found_if_in_even_array(binary_search_function impl) {
+  vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 };
+  for (int i = 0; i < 10; i++) if (impl(a, 10, a[i]) == -1) { return "false"; }
+  return "";
+}
+
+string found_left_in_odd_array(binary_search_function impl) {
+  vector<int> a { 2, 2, 4, 4, 4, 6, 8, 8, 10 };
+  vector<int> r { 0, 0, 0, 2, 2, 5, 5, 6, 6, 8, 8, 9 };
+  for (int i = 0; i < 12; i++) if (impl(a, 9, i) != r[i]) {
+    return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 9, i));
+  }
+  return "";
+}
+
+string found_left_in_even_array(binary_search_function impl) {
+  vector<int> a { 2, 2, 4, 4, 4, 6, 8, 8, 10, 10 };
+  vector<int> r { 0, 0, 0, 2, 2, 5, 5, 6, 6, 8, 8, 10, 10 };
+  for (int i = 0; i < 13; i++) if (impl(a, 10, i) != r[i]) {
+    return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 10, i));
+  }
+  return "";
+}
+
+string found_right_in_odd_array(binary_search_function impl) {
+  vector<int> a { 2, 2, 4, 4, 6, 6, 8, 8, 10 };
+  vector<int> r { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10 };
+  for (int i = 0; i < 10; i++) if (impl(a, 9, i) != r[i]) {
+    return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 9, i));
+  }
+  return "";
+}
+
+string found_right_in_even_array(binary_search_function impl) {
+  vector<int> a { 2, 2, 4, 4, 6, 6, 8, 8, 10, 10 };
+  vector<int> r { 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10 };
+  for (int i = 0; i < 11; i++) if (impl(a, 10, i) != r[i]) {
+    return "esperava encontrar item " + to_string(i) + " na posição " + to_string(r[i]) + " mas obtive " + to_string(impl(a, 10, i));
+  }
+  return "";
+}
+
+string not_found_if_not_found_in_odd_array(binary_search_function impl) {
+  vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16 };
+  for (int i = 0; i < 9; i++) if (impl(a, 9, a[i]+1) != -1) {
+    return "esperava não encontrar item " + to_string(a[i]+1) + " mas obtive " + to_string(impl(a, 9, a[i]+1));
+  }
+  return "";
+}
+
+string not_found_if_not_found_in_even_array(binary_search_function impl) {
+  vector<int> a { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 };
+  for (int i = 0; i < 10; i++) if (impl(a, 10, a[i]+1) != -1) {
+    return "esperava não encontrar item " + to_string(a[i]+1) + " mas obtive " + to_string(impl(a, 10, a[i]+1));
+  }
+  return "";
+}
+
+void perform_tests(
+  vector<pair<string, binary_search_function>> implementations,
+  vector<pair<string, test_binary_search_function>> tests
+) {
+  for (auto test : tests) {
+    string test_name;
+    test_binary_search_function test_function;
+    tie(test_name, test_function) = test;
+
+    cout << "  Teste '" << test_name << "'\n";
+    for (auto implementation : implementations) {
+      string implementation_name;
+      binary_search_function search_function;
+      tie(implementation_name, search_function) = implementation;
+
+      comp = 0;
+      string success = test_function(search_function);
+      cout << "    " << (success == "" ? "\033[32mAC" : "\033[91mWA") << " " << implementation_name;
+      if (success != "") cout << ": " << success;
+      cout << " : " << comp << " comparações" << "\033[0m\n";
+    }
+  }
+}
+
+int main() {
+  cout << "Buscas" << "\n";
+
+  vector<pair<string, binary_search_function>> searches {
+    make_pair("Busca: Intervalo [0, n-1]", busca_intervalo_0_n_1),
+    make_pair("Busca: Intervalo [0, n-1]", busca_intervalo_0_n_1),
+    make_pair("Busca: Intervalo [0, n)", busca_intervalo_0_n),
+    make_pair("Busca: Intervalo [0, n), verificação no fim",
+        busca_intervalo_0_n_end),
+    make_pair("Busca: Intervalo (-1, n)", busca_intervalo__1_n),
+    make_pair("Busca: Intervalo [0, n-1], teto, verificação no fim",
+        busca_intervalo_0_n_1_ceil_end),
+  };
+  vector<pair<string, test_binary_search_function>> search_tests {
+    make_pair("Vetor vazio",
+        not_found_if_array_is_empty),
+      make_pair("Elemento não encontrado em vetor ímpar",
+          not_found_if_not_found_in_odd_array),
+      make_pair("Elemento não encontrado em vetor par",
+          not_found_if_not_found_in_even_array),
+      make_pair("Elemento encontrado em vetor ímpar",
+          found_if_in_odd_array),
+      make_pair("Elemento encontrado em vetor par",
+          found_if_in_even_array),
+  };
+
+  perform_tests(searches, search_tests);
+
+  cout << "Primeiro elemento a esquerda" << "\n";
+
+  vector<pair<string, binary_search_function>> lefts {
+    make_pair("Esquerda: Intervalo [0, n), resposta lo",
+        esquerda_intervalo_0_n_lo),
+    make_pair("Esquerda: Intervalo [0, n), resposta hi",
+        esquerda_intervalo_0_n_hi),
+    make_pair("Esquerda: Intervalo [0, n-1], resposta lo",
+        esquerda_intervalo_0_n_1_lo),
+    make_pair("Esquerda: Intervalo [0, n-1], resposta hi",
+        esquerda_intervalo_0_n_1_hi),
+    make_pair("Esquerda: Intervalo [0, n-1], variável auxiliar",
+        esquerda_intervalo_0_n_1_aux),
+    make_pair("Esquerda: Intervalo [0, n-1], teto, resposta lo",
+        esquerda_intervalo_0_n_1_ceil_lo),
+    make_pair("Esquerda: Intervalo [0, n-1], teto, resposta hi",
+        esquerda_intervalo_0_n_1_ceil_hi),
+    make_pair("Esquerda: Intervalo [0, n-1], teto, variável auxiliar",
+        esquerda_intervalo__1_n_lo),
+    make_pair("Esquerda: Intervalo (-1, n), resposta hi",
+        esquerda_intervalo__1_n_lo),
+    make_pair("Esquerda: lower_bound",
+        esquerda_lower_bound),
+  };
+
+  vector<pair<string, test_binary_search_function>> left_tests {
+      make_pair("Elemento mais a esquerda em vetor ímpar",
+          found_left_in_odd_array),
+      make_pair("Elemento mais a esquerda em vetor par",
+          found_left_in_even_array),
+      make_pair("Elemento encontrado em vetor ímpar",
+          found_if_in_odd_array),
+      make_pair("Elemento encontrado em vetor par",
+          found_if_in_even_array),
+  };
+
+  perform_tests(lefts, left_tests);
+
+  cout << "Primeiro elemento a direita" << "\n";
+
+  vector<pair<string, binary_search_function>> rights {
+    make_pair("Direita: Intervalo [0, n), resposta lo",
+        direita_intervalo_0_n_lo),
+    make_pair("Direita: Intervalo [0, n), resposta hi",
+        direita_intervalo_0_n_hi),
+    make_pair("Direita: Intervalo [0, n-1], resposta lo",
+        direita_intervalo_0_n_1_lo),
+    make_pair("Direita: Intervalo [0, n-1], resposta hi",
+        direita_intervalo_0_n_1_hi),
+    make_pair("Direita: Intervalo [0, n-1], variável auxiliar",
+        direita_intervalo_0_n_1_aux),
+    make_pair("Direita: Intervalo [0, n-1], teto, resposta lo",
+        direita_intervalo_0_n_1_ceil_lo),
+    make_pair("Direita: Intervalo [0, n-1], teto, resposta hi",
+        direita_intervalo_0_n_1_ceil_hi),
+    make_pair("Direita: Intervalo [0, n-1], teto, variável auxiliar",
+        direita_intervalo__1_n_lo),
+    make_pair("Direita: Intervalo (-1, n), resposta hi",
+        direita_intervalo__1_n_hi),
+    make_pair("Direita: upper_bound",
+        direita_upper_bound),
+  };
+
+  vector<pair<string, test_binary_search_function>> right_tests {
+      make_pair("Elemento mais a direita em vetor ímpar",
+          found_right_in_odd_array),
+      make_pair("Elemento mais a direita em vetor par",
+          found_right_in_even_array),
+      make_pair("Elemento encontrado em vetor ímpar",
+          found_if_in_odd_array),
+      make_pair("Elemento encontrado em vetor par",
+          found_if_in_even_array),
+  };
+
+  perform_tests(rights, right_tests);
+}