From 2add026cd9081a3a06dc4103d9862a577da73318 Mon Sep 17 00:00:00 2001
From: Ryan Chou <81596991+ryanchou-dev@users.noreply.github.com>
Date: Thu, 4 Jan 2024 18:12:55 -0800
Subject: [PATCH 1/6] add editorial + cpp code to angry cows
---
solutions/gold/usaco-597.mdx | 155 ++++++++++++++++++++++++++++++++++-
1 file changed, 152 insertions(+), 3 deletions(-)
diff --git a/solutions/gold/usaco-597.mdx b/solutions/gold/usaco-597.mdx
index 7e916f9174..f1df09ff97 100644
--- a/solutions/gold/usaco-597.mdx
+++ b/solutions/gold/usaco-597.mdx
@@ -2,13 +2,27 @@
id: usaco-597
source: USACO Gold 2016 January
title: Angry Cows
-author: Benjamin Qi
+author: Benjamin Qi, Ryan Chou
---
+
+
+For problems where we're asked to minimize/maximize some value, we can ask ourselves these questions:
+
+- How does the "validity" of the answer change as we increase/decrease it?
+- What conditions must be true of an optimal solution?
+
+
+
+Note that for the minimum power that we launch a cow with is monotonic, since there is a set point where smaller values of $R$ will stop working.
+
+This implies a binary search on the power $R$.
+
+
+
[Official Analysis (C++ and Java)](http://www.usaco.org/current/data/sol_angry_gold_jan16.html)
-
The second and third solutions in the analysis fail on the following test case:
```
@@ -18,5 +32,140 @@ The second and third solutions in the analysis fail on the following test case:
```
The second one gives the wrong answer, and the third gives runtime error.
-
+
+From the hints, we'll start binary searching on the initial power $R$. Now, we've reduced the problem into checking if a fixed power $R$ works for our set of haybales.
+
+There are many ways to approach this (see the [Official Analysis](http://www.usaco.org/current/data/sol_angry_gold_jan16.html) for more information!), but perhaps the most straightforward way is running a second binary search on the maximum point we can place our cow.
+
+This works because just like our power, there exists a set point where a cow being launched at a coordinate will not knock out all the haybales to the left/right. Since $N \leq 5 \cdot 10^4$, we can simulate these explosions naively.
+
+
+## Implementation
+
+**Time Complexity**: $\mathcal{O}(N \log^2 N)$
+
+
+
+
+```cpp
+#include
+using namespace std;
+
+using ll = long long;
+#define all(x) begin(x), end(x)
+
+const int MAX_POS = 1e9;
+
+int main() {
+ freopen("angry.in", "r", stdin);
+ freopen("angry.out", "w", stdout);
+
+ int n;
+ cin >> n;
+
+ vector bales(n);
+
+ for (int i = 0; i < n; i++) {
+ cin >> bales[i];
+ // to avoid floats, double all coordinates
+ bales[i] *= 2;
+ }
+
+ sort(all(bales));
+
+ /**
+ * @returns whether it's possible to explode
+ * the rest of the haybales before/after idx
+ *
+ * @param pos position of our cow
+ * @param idx furthest haybale we hit with our radius
+ * @param r current radius at our position
+ * @param dir whether we're checking positions to the right/left
+ */
+ function push = [&](int pos, int idx, int r, bool dir) {
+ // finished search
+ if (idx >= n - 1 && dir == 0) {
+ return (idx >= n || pos + r >= bales[idx]);
+ }
+ if (idx <= 0 && dir == 1) {
+ return (idx < 0 || pos - r <= bales[idx]);
+ }
+
+ if (dir == 0) { // go right
+ if (pos + r >= bales.back())
+ return true;
+
+ int new_idx = idx;
+ while (new_idx < n && pos + r >= bales[new_idx]) {
+ new_idx++;
+ }
+
+ // no progression
+ if (new_idx == idx)
+ return false;
+
+ return push(bales[new_idx - 1], new_idx, r - 2, dir);
+
+ } else { // go left
+ if (pos - r <= bales[0])
+ return true;
+
+ int new_idx = idx;
+ while (new_idx < n && pos - r <= bales[new_idx]) {
+ new_idx--;
+ }
+
+ // no progression
+ if (new_idx == idx)
+ return false;
+
+ return push(bales[new_idx + 1], new_idx, r - 2, dir);
+ }
+
+ return false;
+ };
+
+ int lo = 0, hi = MAX_POS * 2;
+
+ while (lo < hi) {
+ int power = lo + (hi - lo) / 2;
+
+ /*
+ * finds the largest initial position such that
+ * our explosions propagate all the way to the left
+ */
+ int pos_lo = 0;
+ int pos_hi = MAX_POS * 2;
+
+ while (pos_lo < pos_hi) {
+ int pos = pos_lo + (pos_hi - pos_lo + 1) / 2;
+
+ // get index of first haybale to our right
+ int close = lower_bound(all(bales), pos) - bales.begin();
+
+ // is it possible to complete our explosions heading left?
+ if (close < n && push(pos, close, power, 1)) {
+ pos_lo = pos;
+ } else {
+ pos_hi = pos - 1;
+ }
+ }
+
+ int close = upper_bound(all(bales), pos_lo) - bales.begin();
+ // is it possible to complete our explosions heading right?
+ if (push(pos_lo, close, power, 0)) {
+ hi = power;
+ } else {
+ lo = power + 1;
+ }
+ }
+
+ // rescale our answer
+ cout << fixed << setprecision(1) << (double)(lo) / (double)(2.0) << endl;
+}
+```
+
+
+
+
From fb19b945af8fcedc42d68727592b4a1a2b3c497a Mon Sep 17 00:00:00 2001
From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com>
Date: Thu, 4 Jan 2024 18:15:30 -0800
Subject: [PATCH 2/6] Update usaco-597.mdx
---
solutions/gold/usaco-597.mdx | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/solutions/gold/usaco-597.mdx b/solutions/gold/usaco-597.mdx
index f1df09ff97..5f811be83b 100644
--- a/solutions/gold/usaco-597.mdx
+++ b/solutions/gold/usaco-597.mdx
@@ -5,21 +5,25 @@ title: Angry Cows
author: Benjamin Qi, Ryan Chou
---
-
+
For problems where we're asked to minimize/maximize some value, we can ask ourselves these questions:
- How does the "validity" of the answer change as we increase/decrease it?
- What conditions must be true of an optimal solution?
+
+
Note that for the minimum power that we launch a cow with is monotonic, since there is a set point where smaller values of $R$ will stop working.
This implies a binary search on the power $R$.
+
+
[Official Analysis (C++ and Java)](http://www.usaco.org/current/data/sol_angry_gold_jan16.html)
@@ -31,7 +35,7 @@ The second and third solutions in the analysis fail on the following test case:
1
```
-The second one gives the wrong answer, and the third gives runtime error.
+The second one gives the wrong answer, and the third errors.
From the hints, we'll start binary searching on the initial power $R$. Now, we've reduced the problem into checking if a fixed power $R$ works for our set of haybales.
@@ -40,7 +44,6 @@ There are many ways to approach this (see the [Official Analysis](http://www.usa
This works because just like our power, there exists a set point where a cow being launched at a coordinate will not knock out all the haybales to the left/right. Since $N \leq 5 \cdot 10^4$, we can simulate these explosions naively.
-
## Implementation
**Time Complexity**: $\mathcal{O}(N \log^2 N)$
@@ -165,6 +168,7 @@ int main() {
cout << fixed << setprecision(1) << (double)(lo) / (double)(2.0) << endl;
}
```
+
From 2be862cc681103d39bed45b3884523e0fe7adfc2 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 5 Jan 2024 02:15:31 +0000
Subject: [PATCH 3/6] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
solutions/gold/usaco-597.mdx | 29 ++++++++++-------------------
1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/solutions/gold/usaco-597.mdx b/solutions/gold/usaco-597.mdx
index 5f811be83b..1fb7762e63 100644
--- a/solutions/gold/usaco-597.mdx
+++ b/solutions/gold/usaco-597.mdx
@@ -86,42 +86,33 @@ int main() {
* @param r current radius at our position
* @param dir whether we're checking positions to the right/left
*/
- function push = [&](int pos, int idx, int r, bool dir) {
+ function push = [&](int pos, int idx, int r,
+ bool dir) {
// finished search
if (idx >= n - 1 && dir == 0) {
return (idx >= n || pos + r >= bales[idx]);
}
- if (idx <= 0 && dir == 1) {
- return (idx < 0 || pos - r <= bales[idx]);
- }
+ if (idx <= 0 && dir == 1) { return (idx < 0 || pos - r <= bales[idx]); }
if (dir == 0) { // go right
- if (pos + r >= bales.back())
- return true;
+ if (pos + r >= bales.back()) return true;
int new_idx = idx;
- while (new_idx < n && pos + r >= bales[new_idx]) {
- new_idx++;
- }
+ while (new_idx < n && pos + r >= bales[new_idx]) { new_idx++; }
// no progression
- if (new_idx == idx)
- return false;
+ if (new_idx == idx) return false;
return push(bales[new_idx - 1], new_idx, r - 2, dir);
-
+
} else { // go left
- if (pos - r <= bales[0])
- return true;
+ if (pos - r <= bales[0]) return true;
int new_idx = idx;
- while (new_idx < n && pos - r <= bales[new_idx]) {
- new_idx--;
- }
+ while (new_idx < n && pos - r <= bales[new_idx]) { new_idx--; }
// no progression
- if (new_idx == idx)
- return false;
+ if (new_idx == idx) return false;
return push(bales[new_idx + 1], new_idx, r - 2, dir);
}
From 480f5f5f6e96c5bfbe46ee59cc70c2ec70ea8490 Mon Sep 17 00:00:00 2001
From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com>
Date: Thu, 4 Jan 2024 18:54:30 -0800
Subject: [PATCH 4/6] Update usaco-597.mdx
---
solutions/gold/usaco-597.mdx | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/solutions/gold/usaco-597.mdx b/solutions/gold/usaco-597.mdx
index 1fb7762e63..d8cd18303d 100644
--- a/solutions/gold/usaco-597.mdx
+++ b/solutions/gold/usaco-597.mdx
@@ -62,19 +62,15 @@ const int MAX_POS = 1e9;
int main() {
freopen("angry.in", "r", stdin);
- freopen("angry.out", "w", stdout);
int n;
cin >> n;
-
vector bales(n);
-
for (int i = 0; i < n; i++) {
cin >> bales[i];
// to avoid floats, double all coordinates
bales[i] *= 2;
}
-
sort(all(bales));
/**
@@ -120,8 +116,8 @@ int main() {
return false;
};
- int lo = 0, hi = MAX_POS * 2;
-
+ int lo = 0;
+ int hi = MAX_POS * 2;
while (lo < hi) {
int power = lo + (hi - lo) / 2;
@@ -155,8 +151,9 @@ int main() {
}
}
+ freopen("angry.out", "w", stdout);
// rescale our answer
- cout << fixed << setprecision(1) << (double)(lo) / (double)(2.0) << endl;
+ cout << fixed << setprecision(1) << (double)lo / 2 << endl;
}
```
From 9d1dcb7f128ca3538979a6fe760fe10afd75b9be Mon Sep 17 00:00:00 2001
From: Ryan Chou <81596991+ryanchou-dev@users.noreply.github.com>
Date: Thu, 4 Jan 2024 21:45:49 -0800
Subject: [PATCH 5/6] expand
---
solutions/gold/usaco-597.mdx | 38 ++++++++++++++++++++++--------------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/solutions/gold/usaco-597.mdx b/solutions/gold/usaco-597.mdx
index d8cd18303d..bd83fc01df 100644
--- a/solutions/gold/usaco-597.mdx
+++ b/solutions/gold/usaco-597.mdx
@@ -56,7 +56,6 @@ This works because just like our power, there exists a set point where a cow bei
using namespace std;
using ll = long long;
-#define all(x) begin(x), end(x)
const int MAX_POS = 1e9;
@@ -71,7 +70,7 @@ int main() {
// to avoid floats, double all coordinates
bales[i] *= 2;
}
- sort(all(bales));
+ sort(bales.begin(), bales.end());
/**
* @returns whether it's possible to explode
@@ -82,33 +81,42 @@ int main() {
* @param r current radius at our position
* @param dir whether we're checking positions to the right/left
*/
- function push = [&](int pos, int idx, int r,
- bool dir) {
+ function push = [&](int pos, int idx, int r, bool dir) {
// finished search
if (idx >= n - 1 && dir == 0) {
return (idx >= n || pos + r >= bales[idx]);
}
- if (idx <= 0 && dir == 1) { return (idx < 0 || pos - r <= bales[idx]); }
+ if (idx <= 0 && dir == 1) {
+ return (idx < 0 || pos - r <= bales[idx]);
+ }
- if (dir == 0) { // go right
- if (pos + r >= bales.back()) return true;
+ if (dir == 0) { // go right
+ if (pos + r >= bales.back())
+ return true;
int new_idx = idx;
- while (new_idx < n && pos + r >= bales[new_idx]) { new_idx++; }
+ while (new_idx < n && pos + r >= bales[new_idx]) {
+ new_idx++;
+ }
// no progression
- if (new_idx == idx) return false;
+ if (new_idx == idx)
+ return false;
return push(bales[new_idx - 1], new_idx, r - 2, dir);
- } else { // go left
- if (pos - r <= bales[0]) return true;
+ } else { // go left
+ if (pos - r <= bales[0])
+ return true;
int new_idx = idx;
- while (new_idx < n && pos - r <= bales[new_idx]) { new_idx--; }
+ while (new_idx < n && pos - r <= bales[new_idx]) {
+ new_idx--;
+ }
// no progression
- if (new_idx == idx) return false;
+ if (new_idx == idx)
+ return false;
return push(bales[new_idx + 1], new_idx, r - 2, dir);
}
@@ -132,7 +140,7 @@ int main() {
int pos = pos_lo + (pos_hi - pos_lo + 1) / 2;
// get index of first haybale to our right
- int close = lower_bound(all(bales), pos) - bales.begin();
+ int close = lower_bound(bales.begin(), bales.end(), pos) - bales.begin();
// is it possible to complete our explosions heading left?
if (close < n && push(pos, close, power, 1)) {
@@ -142,7 +150,7 @@ int main() {
}
}
- int close = upper_bound(all(bales), pos_lo) - bales.begin();
+ int close = upper_bound(bales.begin(), bales.end(), pos_lo) - bales.begin();
// is it possible to complete our explosions heading right?
if (push(pos_lo, close, power, 0)) {
hi = power;
From 85651f9a5b2e4ac02a6ce9e622bcb16da5377035 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Fri, 5 Jan 2024 05:46:54 +0000
Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
solutions/gold/usaco-597.mdx | 37 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 22 deletions(-)
diff --git a/solutions/gold/usaco-597.mdx b/solutions/gold/usaco-597.mdx
index bd83fc01df..1dbeadba75 100644
--- a/solutions/gold/usaco-597.mdx
+++ b/solutions/gold/usaco-597.mdx
@@ -81,42 +81,33 @@ int main() {
* @param r current radius at our position
* @param dir whether we're checking positions to the right/left
*/
- function push = [&](int pos, int idx, int r, bool dir) {
+ function push = [&](int pos, int idx, int r,
+ bool dir) {
// finished search
if (idx >= n - 1 && dir == 0) {
return (idx >= n || pos + r >= bales[idx]);
}
- if (idx <= 0 && dir == 1) {
- return (idx < 0 || pos - r <= bales[idx]);
- }
+ if (idx <= 0 && dir == 1) { return (idx < 0 || pos - r <= bales[idx]); }
- if (dir == 0) { // go right
- if (pos + r >= bales.back())
- return true;
+ if (dir == 0) { // go right
+ if (pos + r >= bales.back()) return true;
int new_idx = idx;
- while (new_idx < n && pos + r >= bales[new_idx]) {
- new_idx++;
- }
+ while (new_idx < n && pos + r >= bales[new_idx]) { new_idx++; }
// no progression
- if (new_idx == idx)
- return false;
+ if (new_idx == idx) return false;
return push(bales[new_idx - 1], new_idx, r - 2, dir);
- } else { // go left
- if (pos - r <= bales[0])
- return true;
+ } else { // go left
+ if (pos - r <= bales[0]) return true;
int new_idx = idx;
- while (new_idx < n && pos - r <= bales[new_idx]) {
- new_idx--;
- }
+ while (new_idx < n && pos - r <= bales[new_idx]) { new_idx--; }
// no progression
- if (new_idx == idx)
- return false;
+ if (new_idx == idx) return false;
return push(bales[new_idx + 1], new_idx, r - 2, dir);
}
@@ -140,7 +131,8 @@ int main() {
int pos = pos_lo + (pos_hi - pos_lo + 1) / 2;
// get index of first haybale to our right
- int close = lower_bound(bales.begin(), bales.end(), pos) - bales.begin();
+ int close =
+ lower_bound(bales.begin(), bales.end(), pos) - bales.begin();
// is it possible to complete our explosions heading left?
if (close < n && push(pos, close, power, 1)) {
@@ -150,7 +142,8 @@ int main() {
}
}
- int close = upper_bound(bales.begin(), bales.end(), pos_lo) - bales.begin();
+ int close =
+ upper_bound(bales.begin(), bales.end(), pos_lo) - bales.begin();
// is it possible to complete our explosions heading right?
if (push(pos_lo, close, power, 0)) {
hi = power;