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;