Skip to content

Commit 5823f52

Browse files
Merge pull request #4360 from cpinitiative/mobile-o(n)
add o(N) solution to mobile
2 parents 70344d5 + 5602511 commit 5823f52

File tree

6 files changed

+158
-4
lines changed

6 files changed

+158
-4
lines changed

content/3_Silver/Binary_Search.problems.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@
334334
"source": "Baltic OI",
335335
"difficulty": "Very Hard",
336336
"isStarred": false,
337-
"tags": ["Binary Search"],
337+
"tags": ["Binary Search", "Stack"],
338338
"solutionMetadata": {
339339
"kind": "internal"
340340
}

content/4_Gold/Stacks.problems.json

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@
5252
"kind": "internal"
5353
}
5454
},
55+
{
56+
"uniqueId": "baltic-12-mobile",
57+
"name": "2012 - Mobile",
58+
"url": "https://oj.uz/problem/view/BOI12_mobile",
59+
"source": "Baltic OI",
60+
"difficulty": "Normal",
61+
"isStarred": false,
62+
"tags": ["Binary Search", "Stack"],
63+
"solutionMetadata": {
64+
"kind": "internal"
65+
}
66+
},
5567
{
5668
"uniqueId": "usaco-743",
5769
"name": "Modern Art 2",

solutions/silver/baltic-12-mobile.mdx

+145-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
id: baltic-12-mobile
33
source: Baltic OI 2012
44
title: Mobile
5-
author: Andi Qu
5+
author: Andi Qu, Ryan Chou
66
---
77

88
[Official Analysis](https://boi.cses.fi/files/boi2012_solutions.zip)
@@ -64,8 +64,6 @@ a sorted list and still get the correct answer!
6464
This gets rid of the $\log N$ factor in our original complexity and so the final
6565
complexity is $\mathcal{O}(N \log L)$, which is good enough for 100 points.
6666

67-
(There is also an $\mathcal{O}(N)$ solution - try to come up with it yourself!)
68-
6967
## Implementation
7068

7169
```cpp
@@ -100,3 +98,147 @@ int main() {
10098
return 0;
10199
}
102100
```
101+
102+
---
103+
104+
<Info title="Faster Solution">
105+
106+
Although not necessary for full credit, there also exists an $\mathcal{O}(N)$ solution.
107+
108+
Try to come up with it yourself before moving on!
109+
110+
</Info>
111+
112+
<Spoiler title="Faster Solution">
113+
114+
The key observation is that not all stations contribute to the answer. If we ever have a station that isn't the closest to any point on the highway, we can simply ignore it.
115+
116+
In fact, we can also ignore some points on the highway! Once we have this list of key stations, the number of points can be bounded by the number of key stations.
117+
118+
<Spoiler title="How?">
119+
120+
Let's say that we're looking for the point on the highway (excluding the endpoints) with the maximum distance to the nearest station, where there are only two stations, $a$ and $b$.
121+
122+
Then, we can imagine a line perpendicular to the line segment formed by these two stations that passes through the midpoint. Everything on the left side is closer to $a$, and everything on the right side is closer to $b$.
123+
124+
Thus, the maximum point is at the point where the perpendicular line intersects the highway! We only have to get the answer from these critical points, and since there are only two stations that could be closest to it, we can compute them in $\mathcal{O}(1)$.
125+
126+
![Maximum Point](<baltic-12-mobile/perpendicular.png>)
127+
128+
This intersection can be represented by $\frac{b_x^2 + b_y^2 - a_x^2 - a_y^2}{2(b_x - a_x)}$.
129+
130+
From here on, we'll refer to this point as $p(a, b)$, where $a$ and $b$ are stations.
131+
132+
</Spoiler>
133+
134+
Now, let's consider cases where there exist unnecessary stations.
135+
136+
Without loss of generality, let's assume all points are above the y-axis, and $a_x < b_x < c_x$. If not all the points are above the y-axis, we can just flip their sign because the distance to the x-axis remains the same.
137+
138+
### Case 1: $p(a, b) < p(b, c)$
139+
140+
In this arrangement, all stations are the closest to some points on the highway, so we should keep them.
141+
142+
![Case 1](<baltic-12-mobile/case1.png>)
143+
144+
### Case 2: $p(a, b) > p(b, c)$
145+
146+
Note how this creates a cross, which makes $b$ useless. All points on the highway are now closer to $a$ or $c$. To find the maximum point here, we can just remove $b$, and recalculate for $p(a, c)$.
147+
148+
![Case 2](<baltic-12-mobile/case2.png>)
149+
150+
Since we only remove the most recent stations, this motivates the use of a [stack](/gold/stacks) to perform these removals/additions in $\mathcal{O}(1)$.
151+
152+
## Implementation
153+
154+
**Time Complexity**: $\mathcal{O}(N)$
155+
156+
<LanguageSection>
157+
<CPPSection>
158+
159+
```cpp
160+
#include <bits/stdc++.h>
161+
using namespace std;
162+
163+
struct Point {
164+
double x, y;
165+
};
166+
167+
/**
168+
* @return the intersection of the perpendicular line that
169+
* crosses the midpoint of Points a & b with the highway
170+
*/
171+
double max_point(const Point &a, const Point &b) {
172+
return (b.x * b.x + b.y * b.y - a.x * a.x - a.y * a.y) /
173+
(2 * b.x - 2 * a.x);
174+
}
175+
/** @return the euclidean distance between points a & b */
176+
double dist(const Point &a, const Point &b) {
177+
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
178+
}
179+
180+
int main() {
181+
int n, l;
182+
cin >> n >> l;
183+
184+
deque<Point> needed;
185+
for (int i = 0; i < n; i++) {
186+
Point cur;
187+
cin >> cur.x >> cur.y;
188+
cur.y = abs(cur.y);
189+
190+
// always take the one with the lowest y coord
191+
if ((int)needed.size() && needed.back().x == cur.x) {
192+
if (cur.y >= needed.back().y) {
193+
continue;
194+
} else if (cur.y < needed.back().y) {
195+
needed.pop_back();
196+
}
197+
}
198+
199+
// find "crosses"
200+
while ((int)needed.size() > 1 &&
201+
max_point(needed[(int)needed.size() - 2], needed.back()) >
202+
max_point(needed.back(), cur)) {
203+
needed.pop_back();
204+
}
205+
206+
needed.push_back(cur);
207+
}
208+
209+
// out of bounds
210+
while ((int)needed.size() > 1 && max_point(needed[0], needed[1]) < 0) {
211+
needed.pop_front();
212+
}
213+
214+
while ((int)needed.size() > 1 &&
215+
max_point(needed[(int)needed.size() - 2], needed.back()) > l) {
216+
needed.pop_back();
217+
}
218+
219+
double ans = 0;
220+
for (int x = 0; x < (int)needed.size(); x++) {
221+
// get critical points needed[x] is in charge of
222+
Point left = {0, 0};
223+
Point right{l, 0};
224+
225+
if (x) { left.x = max_point(needed[x], needed[x - 1]); }
226+
if (x < (int)needed.size() - 1) {
227+
right.x = max_point(needed[x], needed[x + 1]);
228+
}
229+
230+
if (left.x < 0 || right.x > l || right.x < 0 || left.x > l) {
231+
continue;
232+
}
233+
234+
ans = max({ans, dist(needed[x], left), dist(needed[x], right)});
235+
}
236+
237+
cout << fixed << setprecision(6) << ans << endl;
238+
}
239+
```
240+
241+
</CPPSection>
242+
</LanguageSection>
243+
244+
</Spoiler>
71.7 KB
Loading
66.3 KB
Loading
Loading

0 commit comments

Comments
 (0)