Remove geometry as a source of uncertainty.
Not “learn computational geometry.” The goal is narrower and more useful: if a strong company puts an unfamiliar geometry-flavoured problem in front of you, you recognise the structure, pick the primitive, implement it correctly, and handle the degeneracies — under a clock. Everything in this file was selected against that target and nothing else.
What the research changed about the plan
I pulled the numbers before designing any of this, and two findings reshaped it:
- Pure 2-D geometry is rare — about 1% of company-tagged problems — and it collapses onto roughly six primitives. There is no long tail worth learning.
- The highest-frequency “geometry” carries no geometry tag at all. It is the 1-D projection: intervals and sweep. Merge Intervals is tagged by 21 of 25 companies; the best-represented problem in the actual geometry tag is tagged by 10.
So this is not a computational geometry course with the hard parts removed. It is a different shape from one. The Research tab shows the working.
The plan, and where the stop line is
| Block | Content | Hours | Status |
|---|---|---|---|
| WEEK 1 Core | numeric ritual · cross product · distance & metrics · rectangles · point-set hashing · interval sweep | 12–14 | Non-negotiable. Covers ~85% of observed geometry. |
| WEEK 2 Closing | segments & degeneracies · polygons · angular window · convex hull · Chebyshev rotation · circles | 7–9 | Deferrable behind graphs and DP. Run it in gaps. |
| Maintenance | two problems a week, written from memory, no reference | 0.5/wk | Retention is where this evaporates, so this line is not optional. |
Stop after §7 (interval sweep). Do not start §8–11. A complete, reliable Tier A beats a shaky Tier A+B every time — a primitive you half-remember under a clock is worse than one you know you don’t have, because the half-memory costs you fifteen minutes before you abandon it.
Six sessions
| Session | Read | Then type | Problems |
|---|---|---|---|
| 1 · ~2h | Numeric ritual, §1 distance, §2 cross product | P, cross, orient, dist2 — from scratch, no copying | g1, g4, g5, g6 |
| 2 · ~2h | §3 triangles | pointInTriangle, then run selftest.cpp | g7, g10, plus re-solve your Oracle problem end to end |
| 3 · ~2h | §5 rectangles | overlapArea, overlapAreaValue | g12, g13, g14 |
| 4 · ~2.5h | §6 point-set hashing | slopeKey — this is the one to get exactly right | g8, g9, g11 |
| 5 · ~2h | §7 interval sweep | maxOverlap, and the merge loop | g17, g18, g20 |
| 6 · ~2h | nothing — close the file | the whole toolkit from memory, then diff against toolkit/geo.hpp | Timed Set 1, scored |
How to use the file
- Concepts are open. The Toolkit tab explains everything with nothing hidden. Read it straight through.
- Problems are gated. Every approach, every recognition drill and every timed set hides its answer behind a button. Commit to an answer first, out loud or on paper, then unlock. A wrong guess you then correct is worth more than a right one you were unsure of.
- One part reading to four parts typing. This file is the one part. If you read it end to end without opening an editor you will have learned nothing, and you already know that from the 109 hards.
- Type the toolkit from memory at every session start. Two minutes. It is the difference
between recognising
crossand having it.
The readiness standard
You are ready when you can do all of these, not most:
No curriculum makes an offer more likely by itself, and I am not going to tell you this one does. What it can honestly do is convert one specific variable from luck into preparation. If you fail the standard above, the Reference tab has the repair table: which failure points at which section.
Part I — what geometry actually appears
Everything below was pulled from primary sources on 16 September 2026 and every problem reference was resolved against its source before it was written down. Where the evidence is thin I say so rather than rounding it up.
Method, and its limits
- LeetCode GraphQL problem API. 4,055 public problems; 46 carry the
geometrytag — 1.13% of the corpus. - 25 company tag sets from a public mirror of LeetCode Premium's company tags, snapshot 20 June 2025: Oracle, Google, Microsoft, Amazon, Meta, Apple, Uber, Bloomberg, Adobe, Atlassian, Salesforce, Walmart Labs, Directi, Media.net, Trilogy, Visa, Goldman Sachs, DE Shaw, TikTok, Nvidia, LinkedIn, Flipkart, Citadel, Zoho, Samsung. I cross-referenced these against the 46 by slug and by frequency score.
- Codeforces problemset API. 430 geometry-tagged problems with ratings and solve counts; 160 in the 800–1900 band that is plausibly OA-shaped.
- Kattis for the primitive-drill problems, each slug verified by HTTP.
Tier 1 — repeatedly reported. The company tag frequencies. They are crowd-sourced from candidates who saw a problem in a real process, aggregated over many reports. Directionally reliable, and the only quantitative signal available at this scale.
Tier 3 — reasonable proxy. Codeforces and Kattis ratings and tags. These say nothing about OAs; they say a problem trains a technique.
Known limits, stated plainly. LeetCode tags skew towards companies that use LeetCode-style screens, which under-represents CodeSignal and HackerRank shops. Frequency scores are relative within a company, not across companies. A small tag set — Directi has 8 problems, Trilogy 6 — supports no conclusion at all, and I have not drawn one. And a company tag set is a sample of what candidates chose to report, not a question bank.
Finding 1 — the frequency, company by company
| Company | Problems tagged | Geometry | % | Its top geometry problems |
|---|---|---|---|---|
| 2344 | 31 | 1.32 | 3625 trapezoids (49) · 939 min-area-rect (42) · 963 rotated rect (35) | |
| Amazon | 2011 | 16 | 0.80 | 973 k-closest (47) · 149 max-points (24) · 2101 bombs (18) |
| Meta | 1399 | 14 | 1.00 | 973 k-closest (77) · 1266 min-time (30) · 149 max-points (28) |
| Microsoft | 1384 | 13 | 0.94 | 3009 intersections (27) · 836 rect-overlap (25) · 223 rect-area (22) |
| Bloomberg | 1223 | 11 | 0.90 | 812 largest-triangle (25) · 149 (21) · 973 (21) |
| Uber | 359 | 2 | 0.56 | 3027 place-people-II (52) · 2101 bombs (34) |
| Oracle | 299 | 1 | 0.33 | 973 k-closest (25) |
| Apple | 298 | 2 | 0.67 | 149 (51) · 973 (30) |
| Salesforce | 193 | 1 | 0.52 | 973 k-closest (36) |
| Nvidia | 136 | 3 | 2.21 | 1610 visible-points (68) · 223 (52) · 149 (37) |
| Media.net | 35 | 1 | 2.86 | 1266 min-time (86) — tiny sample, do not over-read |
| Adobe · Atlassian · Visa · Walmart · DE Shaw · Directi · Trilogy · Samsung | — | 0 | 0.00 | no geometry-tagged problem in any of their sets |
Read that last row carefully. Eight of twenty-five companies — including three on your target list — have no geometry in their tag set at all. Geometry is concentrated in Google, Meta, Amazon, Microsoft, Bloomberg and Nvidia. If your pipeline is weighted towards the Indian product companies and services-adjacent shops, the honest expected value of this whole subject is lower than the effort people put into it. Week 1 is the right investment. Three weeks would not be.
Finding 2 — the 1-D projection dominates, and it is not tagged geometry
| Problem | Companies tagging it | Peak frequency | What it really is |
|---|---|---|---|
| LC 56 · Merge Intervals | 21/25 | 93 (Zoho) | 1-D segments, sorted |
| LC 253 · Meeting Rooms II | 17/25 | 84 (TikTok) | event sweep |
| LC 1353 · Max Events That Can Be Attended | 12/25 | 73 (Visa) | sweep + heap |
| LC 729 · My Calendar I | 9/25 | 73 (Uber) | interval overlap, online |
| LC 218 · The Skyline Problem | 8/25 | 39 (Goldman Sachs) | 2-D → sweep with a heap |
| LC 986 · Interval List Intersections | 7/25 | 74 (Meta) | two-pointer on intervals |
| LC 452 · Min Arrows to Burst Balloons | 7/25 | 49 (Zoho) | greedy stabbing |
| — for comparison — | |||
| LC 973 · K Closest Points (best geometry-tagged) | 10/25 | 77 (Meta) | sorting with a metric |
The 1-D projection is two to three times more widely tagged than the best-represented problem in the geometry tag, and ten to twenty times more than the median one. Yet it is filed under sorting, greedy and heap — so people preparing ‘geometry’ skip it, and people preparing sorting never connect it to rectangles and skylines.
That gap is where the actual OA risk lives, and it is why §7 is Tier A rather than an appendix.
Finding 3 — the shape of the geometry that does appear
Of the 46 geometry-tagged problems, sorted by how many companies tag them, the top of the list is: 973 (10), 149 (10), 1266 (6), 223 (6), 939 (4), 2101 (4), 836 (4), 3025 (4), 3047 (4), 2481 (4). Below that, the distribution collapses — 22 of the 46 are tagged by exactly one company, and 8 by none at all.
Grouping those by what they actually require:
| Cluster | Share of the tagged set | Verdict |
|---|---|---|
| Metric + ordinary algorithm — distance as a sort key, a filter, or a graph edge | ~35% | Tier A. The geometry is one line; the problem is sorting, hashing, heap or BFS. |
| Axis-aligned rectangles — overlap, area, finding them in point sets | ~25% | Tier A. Reduces to interval logic on two axes. |
| Cross-product reasoning — collinearity, area, orientation, convexity | ~20% | Tier A. One primitive, many disguises. |
| Sweep in two dimensions — skyline, rectangle union, chart intersections | ~10% | Tier B. Worth the time; the ideas transfer. |
| Genuine advanced CG — hull, minimum enclosing circle, ternary search on a surface | ~10% | Tier C/D. Convex hull earns one evening; the rest earns a name. |
Finding 4 — recency
Restricting to each company's most recent six-month slice in the snapshot, geometry nearly vanishes, and what remains is concentrated: Amazon 2 (149, 973), Bloomberg 2 (973, 149), Google 3 (3464, 223, 963), Meta 1 (973), Microsoft 1 (223). Sixteen of the twenty-five companies show zero geometry in their recent slice.
The recent slices are small, so absence is weak evidence. But the concentration is informative and consistent with the whole-period data: when geometry does show up recently, it is 973, 149 and 223 — k-closest, max-points-on-a-line, and rectangle area. Those three problems are Session 1, Session 3 and Session 4 of the plan, which is how the sessions were ordered.
Coverage matrix
Priorities below come from the evidence above, not from what a geometry textbook would emphasise.
| Skill | Evidence | Tier | Learn | Guided | Mixed | Timed |
|---|---|---|---|---|---|---|
| Squared distance & metrics | LC 973 in 10/25 cos, freq 77; LC 1266 in 6/25 | A | ✓ | ✓ | ✓ | ✓ |
| Cross product / orientation | LC 149 in 10/25; 1037, 1232, 812, 469, 3623 all reduce to it | A | ✓ | ✓ | ✓ | ✓ |
| Triangle degeneracy & containment | your Oracle OA; LC 812, 3588; CF 6A, 1398A | A | ✓ | ✓ | ✓ | ✓ |
| Axis-aligned rectangles | LC 223 in 6/25, 836 in 4/25, 939 in 4/25, 3047 in 4/25 | A | ✓ | ✓ | ✓ | ✓ |
| Point-set hashing | LC 149, 447, 593, 939, 963, 2013, 3623, 3625 | A | ✓ | ✓ | ✓ | ✓ |
| Interval / 1-D sweep | LC 56 in 21/25, 253 in 17/25, 1353 in 12/25, 729 in 9/25 | A | ✓ | ✓ | ✓ | ✓ |
| Segment intersection | LC 335 (Bloomberg) only; strong proxy value on Kattis | B | ✓ | ✓ | ✓ | — |
| Polygon area & containment | LC 812, 469, 587; Kattis; CF 1942C | B | ✓ | ✓ | ✓ | — |
| Angular window (atan2) | LC 1610 — Nvidia freq 68, Google, Amazon; CF 257C | B | ✓ | ✓ | ✓ | ✓ |
| Sweep in 2-D (rect union, skyline) | LC 218 in 8/25, 850 (Flipkart 60), 391 in 3/25, 3009 (Microsoft 27) | B | ✓ | ✓ | ✓ | ✓ |
| Convex hull | LC 587 (Google, Amazon) and LC 1924 (Google) — that is all | C | ✓ | ✓ | — | ✓ |
| Circles | LC 1401, 1828, 2249, 2101 — all reduce to dist2 vs r² | C | ✓ | ✓ | ✓ | — |
| Chebyshev / L1 rotation | LC 3102, 1266, 296, 462; CF 650A | C | ✓ | ✓ | — | — |
| Minimum enclosing circle | LC 1924 (Google, premium), 1453 (Meta) — two problems total | D | name only | — | — | — |
| Rotating calipers | zero occurrences in 25 company tag sets | D | name only | — | — | — |
| Half-plane intersection, Voronoi, Delaunay, k-d trees, 3-D hull, Bentley–Ottmann | zero | D | — | — | — | — |
What is deliberately excluded, and why
| Excluded | Evidence | If an interviewer raises it |
|---|---|---|
| Rotating calipers | 0 occurrences in 25 company sets; the farthest-pair problems that exist have n small enough for O(n²) | “Hull first, then walk antipodal pairs in linear time — but at these constraints I'd brute-force the hull points.” |
| Half-plane intersection | 0 occurrences | Name it, say it is O(n log n) by dual sorting, move on. |
| Voronoi / Delaunay | 0 occurrences | Name it. Nearest-neighbour structure. Never hand-implemented in an interview. |
| k-d trees | appears only as a LeetCode topic label on LC 973, where a heap is the intended answer | Mention it as the n-large structure; give the heap or quickselect answer. |
| Bentley–Ottmann sweep | 0 occurrences | Name it as the O((n+k) log n) all-intersections algorithm. |
| 3-D geometry | only LC 883/892, which are matrix-summation problems in a 3-D costume | — |
| Circle–circle intersection area, tangent lines, circumcircles | 0 occurrences in company sets | Every circle problem in the data is dist2 vs r². |
| Convex hull trick (DP optimisation) | despite the name, a DP technique, not geometry | It belongs in your DP preparation. |
Before any of it: pick your number type
Geometry solutions die in the arithmetic more often than in the idea. Two minutes of this at the top of a problem buys you the rest of it.
The thirty-second ritual
Read the constraint line and find the largest absolute coordinate. Call it C.
- A difference
b.x - a.xis at most2C. - A cross product is a difference times a difference, minus another such product — roughly
2 × (2C)² = 8C². - A squared distance is
(2C)² + (2C)² = 8C². - Compare
8C²against 9.22 × 1018, the ceiling of a signed 64-bit integer.
| C (max coordinate) | 8C² | int (2.1e9) | long long (9.2e18) | verdict |
|---|---|---|---|---|
| 104 | 8 × 108 | ✅ fits | ✅ | int survives; use long long anyway |
| 105 | 8 × 1010 | ❌ overflows | ✅ | long long required |
| 109 | 8 × 1018 | ❌ | ✅ barely — 87% of the range | long long, no headroom left |
| 109, then summed | > 9.2 × 1018 | ❌ | ❌ | __int128, or restructure |
Watch the last row. One cross product at C = 109 fits. A shoelace sum of a
thousand of them does not. When you accumulate, the ceiling applies to the accumulator rather than to
each term.
It compiles without a warning, and it is the most common geometry bug in an OA:
int cross(Point a, Point b, Point c) {
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x); // int arithmetic
}Here it is again wearing a correct return type:
long long cross(Point a, Point b, Point c) {
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x); // STILL int arithmetic
}If x and y are int, every subtraction and every multiplication happens in int. By the time the result widens to long long, the damage is done.
Declare the members long long. Do not cast at the call site. Casts you have to remember get dropped once you are behind the clock.
struct P {
long long x, y; // long long HERE — this is the fix
};
long long cross(P a, P b, P c) { // (b-a) x (c-a)
return (b.x - a.x) * (c.y - a.y)
- (b.y - a.y) * (c.x - a.x);
}
Six rules that cover every OA you will see
| Situation | What to do | Why |
|---|---|---|
| Comparing two distances | compare dist2, never dist | sqrt is monotone on non-negatives, so the order is identical and you stay exact |
| Testing a point against a circle | dx*dx + dy*dy vs r*r | same trick, and it dodges the double equality question at the boundary |
| Deciding left / right / collinear | sign of an integer cross product | the answer is a sign, so it needs no division and no float |
| Polygon or triangle area | keep 2×area as an integer; halve only when printing | twice the area of an integer polygon is always an integer |
| A slope | the reduced pair (dx/g, dy/g) with a fixed sign | double merges slopes that differ and splits slopes that don't |
| A midpoint | store (x1+x2, y1+y2) — the sum | an average needs a /2, which truncates silently on odd sums |
Three situations, and no others in this scope: an angle (atan2), a real-valued answer you have to print, and a ternary search over a continuous parameter. There:
- Use
long double. It costs nothing and buys three or four decimal digits. - Size
EPSagainst the magnitudes in play. At coordinates near 109, an absolute1e-9is finer than the gap between representable doubles up there, so it does nothing whatsoever. - Never compare two floats with
==, and never sort on a float key when an exact integer key exists.
TIER A1 · Distance, and the sqrt you must not write
Distance is the most widely tagged geometry there is, and the technique is one line. The work lies in seeing what is left once you peel the metric off.
Why this comes first
LC 973 · K Closest Points to Origin is tagged by 10 of the 25 companies I pulled — Meta at frequency 77, then Salesforce, Apple, Google, LinkedIn, Oracle, Bloomberg, Goldman Sachs, Microsoft and Amazon. Nothing else in the geometry tag is tagged that widely.
Across the dataset a metric usually sits on top of an ordinary algorithm, serving as a sort key, a filter, or a graph edge. Getting quick at peeling it away is worth more than any individual formula.
The idea
You need to know which of two points is closer, and your hand goes to sqrt. Stop.
sqrt is monotonically increasing on non-negative numbers:
So any question about the order of distances can be settled on squared distances. For integer points those are integers, making the comparison exact and epsilon-free. You need a real square root only when a length has to be printed.
long long dist2(P a, P b) {
long long dx = a.x - b.x, dy = a.y - b.y;
return dx*dx + dy*dy;
}
Three metrics
| Metric | Formula | Unit ball | What signals it in a statement |
|---|---|---|---|
| Euclidean | √(dx² + dy²) | circle | physical distance; the word radius |
| Manhattan (L1) | |dx| + |dy| | diamond | 4-directional grid movement; city blocks |
| Chebyshev (L∞) | max(|dx|, |dy|) | square | 8-directional movement, a king's move, “diagonal steps allowed” |
Separability
Manhattan distance splits into an x-part and a y-part that never interact. An L1 problem in the plane is therefore often two independent one-dimensional problems: solve each axis alone, combine at the end.
Euclidean distance offers no such split. A square root couples the axes, and coordinate-wise reasoning stops being valid the moment one shows up inside a minimisation. This is why the L1 version of a minimisation is frequently easy while the L2 version of the same sentence needs numerical methods, and why the L2 ones make poor OA questions.
Chebyshev separates too, after a change of coordinates:
(u, v) = (x + y, x − y). Chebyshev distance in the original plane becomes half the Manhattan distance in the new one, and the reverse holds as well. A 45°-rotated square turns into an axis-aligned one.// Chebyshev -> Manhattan
long long u = x + y, v = x - y;
// a coupled max(|dx|,|dy|) becomes two ranges you can handle independently
You will not reach for the rotation often. When you do, nothing else substitutes, and it costs four
characters. The tell is a max binding |dx| to |dy|, or a region
described by |x| + |y| ≤ r.
Two worked payoffs of the properties above. Each is the crux of the problems named, which makes them the part worth deriving rather than reading.
- Minimising total L1 distance to a set of points. Separability lets you minimise over x and over y independently, and in one dimension the minimiser of a sum of absolute deviations is the median. The answer is (median of the xs, median of the ys), so the planar problem is the 1-D problem run twice.
- When does Manhattan equal Euclidean? Precisely when
dxordyis zero, meaning the two points share a row or a column. A distance question becomes two hash-map counts and the geometry disappears.
sqrtfollowed by==. Any shape-classification question that compares lengths for equality is exact ondist2and approximate ondouble. Thedoubleversion clears the samples and fails on large coordinates.dist2can overflow wheredistwould not. Squaring is what carries you over the 64-bit ceiling. Run the ritual.
TIER A2 · The cross product
Learn this one properly. Nine of the eleven Tier A/B techniques below are built out of it.
Start from the question
Here is the situation that actually arises. You have a directed line from A to B and a third point C, and you need to know whether C lies to the left of that line, to the right of it, or exactly on it.
Triangle validity, point containment, segment crossing, polygon orientation, convexity and convex hull are all that question asked repeatedly.
cross(A,B,C) answers ‘which side’ with no division, no square root and no epsilon.Deriving it, so you never have to memorise it
Write the two vectors leaving A: u = B − A and v = C − A. The
2-D cross product of two vectors is the scalar
Two properties make it useful, and both are worth holding rather than trusting:
- Its magnitude is the area of the parallelogram spanned by u and v, so half of it is the area of triangle ABC.
- Its sign is positive when v is counter-clockwise from u, negative when clockwise, and zero when the two are parallel — which, for these particular vectors, means A, B and C are collinear.
Substituting u = B − A and v = C − A gives the form you
write:
long long cross(P a, P b, P c) { // sign: + left, - right, 0 collinear
return (b.x - a.x) * (c.y - a.y)
- (b.y - a.y) * (c.x - a.x);
}
int orient(P a, P b, P c) { // squashed to -1 / 0 / +1
long long v = cross(a, b, c);
return (v > 0) - (v < 0);
}
You could answer ‘which side’ by comparing slopes: is (c.y-a.y)/(c.x-a.x) greater than (b.y-a.y)/(b.x-a.x)? Multiply through by the two denominators and the cross product drops out. They are the same inequality.
The slope version carries two divisions, so it needs a zero-denominator case for vertical lines, floating point, and an epsilon. The cross product has already cleared the denominators. Once you have seen that, it stops being a formula you have to trust.
What this one function answers
| Question | Expressed with cross |
|---|---|
| Are A, B, C collinear? | cross(a,b,c) == 0 |
| Do A, B, C form a real triangle? | cross(a,b,c) != 0 |
| Area of triangle ABC | abs(cross(a,b,c)) (that is 2×area) |
| Is the turn at B a left turn? | cross(a,b,c) > 0 |
| Is this polygon convex? | all consecutive crosses share one sign |
| Was the polygon given clockwise? | sign of the shoelace sum |
| Do segments AB and CD cross? | four orientation tests (§4) |
| Is P inside triangle ABC? | three crosses, signs compared (§3) |
- Argument order.
cross(a,b,c)andcross(b,a,c)carry opposite signs. Fix one convention — first two arguments are the directed line, third is the query point — and hold it everywhere, including inside loops where reordering looks harmless. intoperands. Covered above. Worth repeating because it costs whole problems rather than single test cases.- Writing
> 0when the shape might be clockwise. A test that assumes counter-clockwise input inverts silently on clockwise input. Either normalise the winding first or compare the three signs against each other rather than against zero.
TIER A3 · Triangles
Non-degeneracy, area, containment — and the reason your Oracle problem’s own framing worked against you.
The problem you lost
Three points A, B, C and two query points P, Q. Decide whether ABC is a non-degenerate triangle,
then whether P and Q lie inside it. The statement framed validity through side-length inequalities:
a + b > c, and the other two.
That framing is correct mathematics and a bad computational plan. Follow it literally and you take three square roots, compare three floating-point sums, and inherit an epsilon problem — all for a question whose answer is one integer compared against zero.
You knew the cross product. What you needed was to hear “valid triangle” and think “not collinear”. That translation is the trainable part, and the Recognition tab drills it.
Non-degeneracy
bool isTriangle(P a, P b, P c) {
return cross(a, b, c) != 0; // exact: no sqrt, no epsilon, no side lengths
}
One test covers the cases the side-length form makes you enumerate by hand. Two coincident points,
three identical points, and three points on a line all give cross == 0, and all get
rejected.
If the input is a list of lengths rather than points — sticks, rods, edges with no coordinates — then a cross product has nothing to act on and the triangle inequality is all you have. Sorting usually follows.
Points give you the cross product. Lengths give you the inequality. Reading which one you were handed is half the recognition skill, and statements move between them freely.
Area
long long twiceArea(P a, P b, P c) {
return llabs(cross(a, b, c)); // keep it doubled — stays an integer
}
Halve at the very end, and only to print. If the problem compares or maximises areas, don't halve at all — the comparisons agree either way, and you stay in exact integers.
Containment
The version below reasons about the three signs instead of assuming a counter-clockwise triangle, which is what lets it survive arbitrary input:
// returns 1 = strictly inside, 0 = on the boundary, -1 = outside
// precondition: a, b, c is a non-degenerate triangle (check first!)
int pointInTriangle(P a, P b, P c, P p) {
int d1 = orient(a, b, p);
int d2 = orient(b, c, p);
int d3 = orient(c, a, p);
bool hasNeg = (d1 < 0) || (d2 < 0) || (d3 < 0);
bool hasPos = (d1 > 0) || (d2 > 0) || (d3 > 0);
if (hasNeg && hasPos) return -1; // signs disagree -> outside
if (d1 == 0 || d2 == 0 || d3 == 0) return 0; // touching an edge or vertex
return 1; // all strictly same sign
}
“Inside” is ambiguous and statements resolve it differently. Find the sentence that says whether a point on an edge counts, and whether a vertex counts. Decide what this function returns for each, and put it in a comment before you write the body.
Returning tri-state 1/0/-1 rather than a bool keeps that decision a one-line change later instead of a rewrite.
A popular alternative says P is inside iff area(ABP) + area(BCP) + area(CAP) == area(ABC). True, and it costs more:
- Three absolute values and an equality test against three signs — more arithmetic, and more of your overflow headroom spent.
- Written in
doubleit fails at the boundary, which is where the hidden tests live. - It cannot separate ‘on an edge’ from ‘strictly inside’ without extra work, and that distinction is usually what the question turns on.
Your recovery for that OA
// 1. degenerate?
if (cross(A, B, C) == 0) { cout << "not a triangle"; return; }
// 2. each query point
for (P q : {P_, Q_}) {
int r = pointInTriangle(A, B, C, q);
cout << (r == 1 ? "inside" : r == 0 ? "on boundary" : "outside") << '\n';
}
TIER A5 · Axis-aligned rectangles
By the company data, the largest block of genuine 2-D geometry that gets asked — and the block with the least geometry inside it.
The one idea
An axis-aligned rectangle is a pair of intervals, one on x and one on y. Two rectangles overlap exactly when their x-intervals overlap and their y-intervals overlap. No 2-D reasoning is involved at any point.
struct R { long long x1, y1, x2, y2; }; // x1<x2, y1<y2
// do they share positive AREA? (touching edges do NOT count)
bool overlapArea(R a, R b) {
return max(a.x1, b.x1) < min(a.x2, b.x2)
&& max(a.y1, b.y1) < min(a.y2, b.y2);
}
// do they touch or overlap? (shared edge or corner DOES count)
bool overlapClosed(R a, R b) {
return max(a.x1, b.x1) <= min(a.x2, b.x2)
&& max(a.y1, b.y1) <= min(a.y2, b.y2);
}
// area of the overlap (0 if none)
long long overlapAreaValue(R a, R b) {
long long w = min(a.x2, b.x2) - max(a.x1, b.x1);
long long h = min(a.y2, b.y2) - max(a.y1, b.y1);
return (w > 0 && h > 0) ? w * h : 0;
}
Those two functions differ by two characters and disagree on exactly the test case a grader cares about. Some statements say rectangles sharing only an edge do not overlap; others say they do. Find that sentence, then write the comparator to match it.
Samples rarely contain a tangency, so the mistake stays invisible until the hidden tests.
Notice that overlapAreaValue clamps each axis separately. Multiplying two negative
extents together produces a positive phantom overlap, which is the other way this gets you.
Inclusion–exclusion then gives the union of two rectangles:
long long areaUnion(R a, R b) {
long long A = (a.x2-a.x1)*(a.y2-a.y1);
long long B = (b.x2-b.x1)*(b.y2-b.y1);
return A + B - overlapAreaValue(a, b);
}
At three or more rectangles inclusion–exclusion needs 2n terms and you switch to a sweep, covered in §7. Recognising where that switch happens is most of the skill.
Rectangles hidden in a point set
A second and very common family hands you points and asks about rectangles they form. The geometry is trivial; the work is choosing a hash key and deciding which pairs to iterate over.
Three reusable ideas, stated without reference to any particular problem:
- Put every point in a hash set, then iterate over pairs you treat as a diagonal and look up the implied remaining corners. O(n²), and usually what the constraints intend.
- For rectangles at arbitrary angles, lean on a property that never mentions the axes — see the midpoint and length keys in §6.
- When a whole layout has to be verified, look for a pair of cheap invariants that are jointly sufficient instead of one expensive geometric check. Two weak, fast conditions often pin down a configuration that neither pins down alone, and that pattern travels well beyond geometry.
The four concrete instantiations, for checking your reasoning after you have attempted them.
| Shape of question | The move |
|---|---|
| smallest axis-aligned rectangle from a point set | hash the points; for each pair with differing x and differing y, treat it as a diagonal and look up (x1,y2) and (x2,y1) |
| smallest rectangle at any angle | two segments are the diagonals of a rectangle iff they share a midpoint and a length; group point pairs by (x1+x2, y1+y2, dist2) |
| counting squares against streamed points | count by diagonal — for each query point, walk stored points with |dx| == |dy| ≠ 0 and multiply the counts of the two missing corners |
| do these rectangles tile a perfect rectangle exactly | the two sufficient invariants are total area equal to the bounding box, and corner parity: toggle all four corners of every rectangle in a set, and exactly the four outer corners must survive. Area alone permits a gap cancelling an overlap; parity alone permits other degenerate layouts |
TIER A6 · Point-set hashing
Geometry that is really a hash map. What these problems test is exact integer normalisation.
Canonical slope
LC 149 · Max Points on a Line is tagged by 10 of 25 companies — LinkedIn at 56, then Apple, Citadel, Zoho, Nvidia, Meta, Google, Amazon, Bloomberg and Microsoft. Its acceptance rate is 31%, which is low for a problem whose algorithm is short and fairly obvious. The difficulty sits entirely in how you represent a slope.
double s = (double)dy / dx; loses to three separate things: division by zero on vertical lines, two genuinely different slopes rounding to the same double, and one slope computed from two different pairs rounding to two different doubles. All three turn up in real test sets.
// canonical direction of the line through a and b
pair<long long,long long> slopeKey(P a, P b) {
long long dx = b.x - a.x, dy = b.y - a.y;
long long g = std::gcd(llabs(dx), llabs(dy));
if (g) { dx /= g; dy /= g; }
// force one representative per direction
if (dx < 0 || (dx == 0 && dy < 0)) { dx = -dx; dy = -dy; }
return {dx, dy};
}
Read the sign-fixing line closely, because correctness lives there. After gcd reduction
(2,3) and (-2,-3) describe the same line but hash differently. “If dx
is negative, flip both” repairs that — except when dx == 0, where flipping on
dx's sign does nothing and (0,5) and (0,-5) stay apart. Hence the second
clause. Vertical lines are what people forget.
std::gcd(0,0) is 0, and dividing by it is undefined behaviour. The if (g) guard covers coincident points. When duplicates are possible in the input, count them separately and add them to every line's total. Whether they are possible is a constraint-line question, and the answer varies.
One level up: canonical line
A slope identifies a direction. To count distinct lines, or to detect parallel ones,
use the implicit form Ax + By + C = 0 with A = dy, B = -dx and
C = -(A·x₁ + B·y₁), then reduce the whole triple by
gcd(|A|,|B|,|C|) and fix the sign the same way.
CF 1163C1 · Power Transmission (Easy Edition) and its hard version drill this directly.
Nearly every point-set counting problem in the dataset is one of these dropped into a map, followed by a k·(k−1)/2.
- Direction:
(dx, dy)reduced by gcd, sign fixed. - Length:
dist2, neverdist. - Midpoint:
(x₁+x₂, y₁+y₂), the sum, so it stays integral.
Combining two of them identifies a shape. Segments sharing a midpoint are the diagonals of a parallelogram; sharing a midpoint and a length makes it a rectangle; add perpendicularity and it is a square. Derive the rest from there.
Grouping by exact distance
A family of shape-classification and counting questions reduces to this: compute all pairwise
dist2 from an anchor, group them, count within groups. Everything stays integral, so
equality is real equality rather than an approximation.
Decide whether the problem counts ordered or unordered pairs before you write
k*(k-1) or k*(k-1)/2. That factor of two is a frequent wrong answer.
TIER A7 · The 1-D projection — intervals and sweep
The highest-frequency ‘geometry’ in the dataset, carrying no geometry tag. Most likely to be tested, least likely to be filed under geometry.
What the data said
Across the 25 company tag sets: LC 56 · Merge Intervals appears in 21. LC 253 · Meeting Rooms II in 17. LC 1353 · Maximum Number of Events That Can Be Attended in 12. LC 729 · My Calendar I in 9. LC 218 · The Skyline Problem in 8.
Inside the geometry tag proper, the best-represented problem appears in 10 sets and the median appears in 1. If your hours are limited, spend them here.
Why it belongs in a geometry file
Because it is geometry, collapsed. A meeting is a segment on a time line. A building in a skyline is a rectangle. A rectangle in a union-of-areas problem is an interval that stays alive over a range of y. Once you notice that a 2-D problem only ever asks about one axis at a time, it reduces to this.
// max number of simultaneously-alive intervals
int maxOverlap(vector<pair<long long,long long>> iv) {
vector<pair<long long,int>> ev;
for (auto [s, e] : iv) { ev.push_back({s, +1}); ev.push_back({e, -1}); }
sort(ev.begin(), ev.end()); // ties: -1 sorts before +1, so touching does NOT overlap
int cur = 0, best = 0;
for (auto [x, d] : ev) { cur += d; best = max(best, cur); }
return best;
}
When one interval ends exactly where another begins, do they overlap? The code above says no: pair sorting puts {{x,-1}} before {{x,+1}}, so the close is processed first. Encode the events as {{x,0}} for open and {{x,1}} for close and you get the opposite convention.
Both are correct for some problem and neither is correct for all of them. A meeting ending at 10:00 does not conflict with one starting at 10:00, but two segments touching at a point usually do count as touching. Read the statement, choose, and write the reason in a comment so you do not flip it back later when a sample appears to disagree.
The four moves
| Move | Cost | Use when |
|---|---|---|
| Sort by start, merge as you go | O(n log n) | you need the merged intervals themselves |
| Event sweep with a counter | O(n log n) | you need a count, a maximum, or an integral |
| Sweep with a heap | O(n log n) | you need to know which interval is on top right now |
| Difference array / prefix sums | O(n + range) | coordinates are small and dense |
Lifting it back to 2-D
For the area of a union of many rectangles, sweep a vertical line across x. At any x the shape is a
set of y-intervals, and the area between consecutive x-events is
(total covered y-length) × (Δx). The new ingredient is coordinate
compression: only y values appearing as rectangle edges can change the covered length, so there
are at most 2n of them.
The textbook version maintains covered length in O(log n) using a segment tree with a lazy count of covering intervals. You do not need that here. At OA sizes, recomputing covered length by scanning the compressed y-array is O(n) per event and O(n²) overall, which passes and which you can write correctly under pressure.
Being able to name the segment-tree version is worth a sentence in an interview. Implementing it is three hours you need for graphs.
TIER B4 · Segments
Tier B: dedicated segment-intersection questions are uncommon in the company data. The degenerate cases are still the best edge-case training in the subject, and ‘do the paths cross’ is exactly the wording that produces a blank page.
Point on segment
Two conditions, both required. Collinearity puts the point somewhere on the infinite line; the bounding box clips it back to the segment.
bool onSegment(P a, P b, P p) { // is p on segment ab (endpoints included)?
if (cross(a, b, p) != 0) return false; // not even on the line
return min(a.x, b.x) <= p.x && p.x <= max(a.x, b.x) // inside the box
&& min(a.y, b.y) <= p.y && p.y <= max(a.y, b.y);
}
Order matters here. A box test on its own admits points off the line, but a box test applied
after the collinearity test cannot produce a false positive: a point both on the line and
inside the box is necessarily on the segment. A zero-length segment (a == b) also works
with no special case — the cross is zero for every p, and the box collapses to a single
point.
Do two segments intersect?
The general case first. Segments AB and CD cross properly when C and D lie on opposite sides of line AB, and A and B lie on opposite sides of line CD. Both halves are needed — the bottom-right panel is exactly where one holds and the other does not.
bool segIntersect(P a, P b, P c, P d) {
int d1 = orient(a, b, c), d2 = orient(a, b, d);
int d3 = orient(c, d, a), d4 = orient(c, d, b);
// proper crossing: strictly opposite sides, both ways
if (d1 * d2 < 0 && d3 * d4 < 0) return true;
// degenerate: an endpoint lies on the other segment
if (d1 == 0 && onSegment(a, b, c)) return true;
if (d2 == 0 && onSegment(a, b, d)) return true;
if (d3 == 0 && onSegment(c, d, a)) return true;
if (d4 == 0 && onSegment(c, d, b)) return true;
return false;
}
- Touching at an endpoint. One orientation is 0, so
d1*d2 < 0fails and the proper test answers no. Two paths meeting at a junction is a very natural thing for a statement to describe. - Collinear and overlapping. All four orientations are 0, and again the proper test answers no.
- Collinear and disjoint. All four orientations are also 0 — so returning true whenever everything is collinear fails in the opposite direction. The
onSegmentcalls are what tell these two apart. - A zero-length segment against another drops straight out of the
onSegmentbranch.
The d1*d2 product is safe because orient already squashed to -1/0/1. Inline raw cross values there and you are multiplying two numbers near 8×1018.
Rarely asked, because it is rational rather than integral and the statement would then have to specify a tolerance. If you do need it, use the parameter form in long double:
// assumes the segments are known to intersect and are not parallel
long double t = (long double)cross(a, c, d) / cross2(b - a, d - c);
// point = a + t * (b - a)Noticing that the answer is rational, and therefore probably not what is being asked for, is a useful reading habit by itself.
Drills: Kattis · Line Segment Intersection forces every degenerate case, including printing a segment when the overlap is a segment. Kattis · Line Segment Distance pairs with it.
A common disguise: a path turns repeatedly and you must decide whether it ever re-crosses itself. These usually admit two solutions — an O(n²) all-pairs segment check, and a clever O(n) case analysis over consecutive turns.
Under a clock, write the O(n²) one unless n forbids it. The case analysis is short to read and easy to get subtly wrong, and you will have no reference model to check it against.
TIER B8 · Angular reasoning
The one place floating point is unavoidable. Worth learning because the ‘double the array’ trick transfers to every circular problem you will meet.
The shape of the question
You stand at a point with a field of view of θ degrees that you may aim
anywhere, and you want the most you can see at once. Variants ask for the smallest angle covering
everything, or the largest empty wedge. Same machine each time.
int maxInWindow(vector<P>& pts, double angleDeg, P you) {
vector<double> a;
int same = 0; // points exactly ON you
for (P p : pts) {
if (p.x == you.x && p.y == you.y) { same++; continue; } // no angle exists
a.push_back(atan2((double)(p.y - you.y), (double)(p.x - you.x)) * 180.0 / M_PI);
}
sort(a.begin(), a.end());
int n = a.size();
for (int i = 0; i < n; i++) a.push_back(a[i] + 360.0); // the doubling
int best = 0;
for (int l = 0, r = 0; r < (int)a.size(); r++) {
while (a[r] - a[l] > angleDeg + 1e-9) l++; // EPS: boundary counts as inside
best = max(best, r - l + 1);
}
return min(best, n) + same;
}
- Points coincident with the observer.
atan2(0,0)returns 0 instead of failing, which quietly files them at 0° and yields a wrong answer that clears the samples. They have no direction relative to you, so handle them outside the window entirely. - The wrap-around. A window running from 350° to 10° is real, and the doubled array is what makes it cost no code.
- Epsilon direction. If a point exactly on the edge of the view counts as seen, the tolerance has to widen the window:
> angle + 1e-9. Subtract instead of add and every tangency case fails. Read the boundary sentence, then aim the epsilon at it.
Use atan2 only when you need the magnitude of an angle: a field of view, an arc, a difference between two directions.
Never use it to answer a comparison the cross product can answer. “Is C left of AB”, “are these directions the same” and “sort these points by angle around the origin” are exact integer questions, and routing them through atan2 converts an exact answer into an approximate one for nothing. Angular sorting is done with a half-plane bucket plus a cross product.
Drill: CF 257C · View Angle inverts the objective, and makes a good second rep once you have done a first.
TIER B10 · Polygons
Shoelace is cheap. Ray casting has one genuinely nasty edge case, and meeting it here costs less than meeting it in the hidden tests.
Shoelace
long long twicePolygonArea(const vector<P>& v) { // SIGNED: + is CCW, - is CW
long long s = 0;
int n = v.size();
for (int i = 0; i < n; i++) {
P a = v[i], b = v[(i+1) % n];
s += a.x * b.y - a.y * b.x; // cross of the two position vectors
}
return s; // abs() for area; sign for orientation
}
Two uses out of one function: llabs(s) is twice the area, and s > 0
says the vertices arrived counter-clockwise. Check that orientation before running anything that
assumes a winding direction.
Each term is about 2C² and you are adding n of them. At C = 109 with n = 1000 the sum reaches 1021 and overflows long long even though no single term does. Either accumulate in __int128, or translate the polygon so its first vertex sits at the origin, which shrinks every term to the polygon's own extent. The translation is free and usually removes the problem outright.
Point in polygon
For a convex polygon, all consecutive cross products from the query point share a sign. Simple, exact, O(n).
For a general simple polygon, cast a ray (conventionally +x) and count crossings; odd means inside. The awkward part is a ray passing exactly through a vertex, which would otherwise be counted twice or not at all.
// returns true if p is strictly inside; boundary handled separately by onSegment
bool inPolygon(const vector<P>& v, P p) {
int n = v.size(); bool in = false;
for (int i = 0, j = n - 1; i < n; j = i++) {
if (onSegment(v[j], v[i], p)) return true; // boundary: decide your convention
// the half-open y test is what makes vertices count exactly once
bool straddles = (v[i].y > p.y) != (v[j].y > p.y);
if (!straddles) continue;
// x of the crossing, compared without dividing
long long dx = v[j].x - v[i].x, dy = v[j].y - v[i].y;
long long lhs = (p.y - v[i].y) * dx;
long long rhs = (p.x - v[i].x) * dy;
if (dy > 0 ? lhs > rhs : lhs < rhs) in = !in; // flip comparison when dy is negative
}
return in;
}
(v[i].y > p.y) != (v[j].y > p.y)is a half-open test. An edge counts when it straddles the ray's height with one endpoint strictly above and one not-above. The asymmetry is deliberate: it assigns a vertex sitting exactly at the ray's height to exactly one of its two edges. Writing≥on both sides breaks it.- Flipping the comparison when
dy < 0. Cross-multiplying an inequality by a negative number reverses it. Omit the flip and the function is correct for roughly half the edges.
Drills with exact feedback: Kattis · Point in Polygon and Kattis · Polygon Area.
TIER C11 · Circles
Circles appear. Circle geometry mostly does not.
Every circle-flavoured problem in the company data reduces to comparing dist2 against
r². Not one needs circle–circle intersection area, tangent lines, or a
circumcircle. Keep everything squared and everything stays integral.
| Question | The test |
|---|---|
| Is point P inside circle (C, r)? | dist2(P,C) ≤ r*r |
| Do circles (A,r) and (B,s) overlap? | dist2(A,B) ≤ (r+s)*(r+s) |
| Is one circle inside the other? | dist2(A,B) ≤ (r-s)*(r-s), with r > s |
| Does circle (C,r) meet an axis-aligned rectangle? | clamp C into the rectangle, then dist2(C, clamped) ≤ r*r |
The last row deserves a second look, because the clamp is the nearest point:
// nearest point of an axis-aligned rectangle to a point
long long cx = max(r.x1, min(c.x, r.x2));
long long cy = max(r.y1, min(c.y, r.y2));
bool hit = (c.x-cx)*(c.x-cx) + (c.y-cy)*(c.y-cy) <= rad*rad;
No case analysis, and no separate handling for a centre that lies inside the rectangle.
When a problem gives objects a range and asks what reaches what, the geometry is a one-line predicate and everything after it is a graph traversal. Build edges with dist2 ≤ r², then run whatever the question wants: reachability, components, shortest path.
Check whether the edge is directed. If each object carries its own radius, a long-range one can reach a short-range one without the reverse holding, and the relation is asymmetric. People lose these problems on the asymmetry rather than on the distance.
TIER C9 · Convex hull
Tier C, and I will be straight about why it is here at all: it appears in two problems across 25 company tag sets. It costs one evening, it is built entirely from §2, and it is the one advanced-sounding name that does turn up at Google.
The honest frequency
LC 587 · Erect the Fence — Google and Amazon. LC 1924 · Erect the Fence II — Google, premium, and it is minimum enclosing circle rather than hull. That is the complete list.
Learn it, do two problems, move on. Hull variants are not where your remaining hours belong.
vector<P> convexHull(vector<P> p, bool keepCollinear) {
sort(p.begin(), p.end(), [](P a, P b){
return a.x != b.x ? a.x < b.x : a.y < b.y;
});
p.erase(unique(p.begin(), p.end(), [](P a, P b){
return a.x == b.x && a.y == b.y; // duplicates break the pop test
}), p.end());
if (p.size() < 3) return p;
vector<P> h;
for (int pass = 0; pass < 2; pass++) { // lower, then upper
size_t start = h.size();
for (P q : p) {
while (h.size() >= start + 2) {
long long c = cross(h[h.size()-2], h[h.size()-1], q);
if (keepCollinear ? (c < 0) : (c <= 0)) h.pop_back();
else break;
}
h.push_back(q);
}
h.pop_back(); // last point starts the next pass
reverse(p.begin(), p.end());
}
return h;
}
cross <= 0 pops collinear points and returns a hull of corners only. cross < 0 keeps them and returns every point on the boundary.
Which you want depends on the wording. A question about the shape — its area, its perimeter, how far it extends in some direction — wants corners. A question about which objects lie on the boundary wants all of them, since something sitting exactly on the boundary line is still on the boundary. Choose consciously each time rather than reusing whichever version you wrote last; getting this backwards is the one way to fail the function.
What a hull buys you
- Farthest pair. Both endpoints always lie on the hull, so you can hull first and then check pairs among far fewer points. At OA sizes the brute-force check over hull points is fine.
- Any extremal question: the point furthest in a given direction, the smallest enclosing convex shape, whether two point sets can be separated by a line.
- Convexity testing, although checking that consecutive cross products share a sign is cheaper and simpler than building a hull to compare against.
These are real computational geometry and they sit outside your scope, because they appear zero times in the company data and near-zero in the OA-range Codeforces band: rotating calipers, half-plane intersection, Voronoi diagrams, Delaunay triangulation, k-d trees, Bentley–Ottmann, Minkowski sums, 3-D hulls, and the convex hull trick for DP optimisation, which is a DP technique that borrowed the name.
If an interviewer raises one, naming it and saying what it does beats a half-remembered implementation. Preparing them is the mistake this file exists to prevent.
Part III — recognition
The gap you named after the Oracle OA was not knowledge. You knew the cross product. You did not connect “is this a valid triangle” to it fast enough. That connection is a trainable reflex, and this is the drill for it.
The chain to run, every time
Four worked examples of the chain, so the shape is concrete before you drill:
Decide the numeric type while you are still recognising the problem. Defer it until you are typing and you will default to int and find out at the hidden tests. Say the type out loud when you name the primitive.
Twenty-four drills
No topic labels, deliberately mixed order. Commit to an answer before unlocking — a wrong guess that you then correct is worth more than a right guess you were not sure of, and far more than reading the answer first. Target: under ninety seconds each.
1. “Given three vertices, determine whether they can form a triangle with positive area.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Cross product ≠ 0. Do not go near side lengths — you have coordinates, so collinearity is the question and it is exact.
2. “You are given the lengths of n sticks. Can any three form a triangle?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Triangle inequality after sorting. No coordinates exist, so there is nothing for a cross product to act on. Sort and check adjacent triples — if any triple works, an adjacent one does. The twin of r1; do them as a pair.
3. “A delivery drone travels between cells and may move diagonally. Minimum moves?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Chebyshev: max(|dx|, |dy|). ‘May move diagonally’ is the tell. 4-directional would be Manhattan.
4. “Find the pair of servers with the smallest latency, where latency is Euclidean distance.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Squared distance. Then it is just a minimum over pairs — or divide and conquer if n is large. The word ‘Euclidean’ is not an instruction to call sqrt.
5. “How many of the n antennas can be covered by rotating a directional beam of 45°?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Angular sliding window. atan2, sort, append +360, two pointers. Antennas at your exact position are always covered and must be counted separately.
6. “Given m axis-aligned billboards, what total wall area do they cover?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Sweep + coordinate compression. Two billboards would be inclusion–exclusion; m of them makes that 2m terms, so you sweep one axis and measure covered length on the other.
7. “Each sensor has a range r. Sensor A can trigger sensor B if B is within A’s range. How many fire if you trigger one?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
A graph problem with a dist2 ≤ r² edge predicate. The edge is directed — a long-range sensor reaches a short-range one without the reverse. Then DFS/BFS.
8. “Determine the largest number of points that lie on a common straight line.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Canonical slope hashing. gcd-reduced pair, sign fixed, anchored at each point. double slope fails the official tests outright.
9. “Two robots follow given paths. Do they ever occupy the same position?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Read the timing clause first. If they must be there simultaneously, it is simulation or a per-timestep comparison. If it merely asks whether the paths touch, it is segment intersection with every degenerate case live. These are different problems and the statement distinguishes them in one sentence.
10. “Given start and end times of n jobs, find the minimum number of machines.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Event sweep. Maximum number of simultaneously alive intervals. Decide the touching convention from the statement before writing the comparator.
11. “Find the smallest axis-aligned rectangle, with sides parallel to the axes, whose four corners are all in the given point set.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Hash set + iterate over diagonals. O(n²) pairs, check the two implied corners. The geometry is nil; the data structure is everything.
12. “Determine whether a given point lies within a plot of land defined by its boundary vertices.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Point in polygon. Convex → consistent cross-product signs. General → ray casting with the half-open y test. And find the sentence about points on the boundary before you start.
13. “A camera at the origin has a circular field of radius r. Does it see any part of the rectangular room?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Clamp the centre into the rectangle, then dist2 ≤ r². The clamped point is the nearest point of the rectangle. No case analysis needed.
14. “Count pairs of points at equal distance from a given anchor.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Bucket by dist2, then k·(k−1)/2 per bucket (or k·(k−1) if the problem counts ordered pairs — check).
15. “Given n line segments on a number line, find the point covered by the most of them.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
1-D sweep with a counter. This is the same machine as r10 with different nouns. Noticing recognising the repeat is the drill.
16. “The minimum fencing needed to enclose all the trees.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Convex hull, then sum the edge lengths. ‘Enclose all’ plus ‘minimum perimeter’ is the hull’s defining property, and this phrasing is how it shows up.
17. “Determine whether the polygon described by these vertices is convex.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
All consecutive cross products share one sign. Do not build a hull and compare — that is O(n log n) to answer an O(n) question, and the comparison itself is fiddly.
18. “Maximise the number of points inside an axis-aligned square of fixed side k that you may place anywhere.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Sort by x, two-pointer window of width k, and within that window a second pass on y. A fixed-size box is two coupled sliding windows. If the square is rotated 45°, apply the Chebyshev rotation first.
19. “Given n rectangles, is there a point covered by all of them?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Intersect the intervals on each axis independently. max(x1) ≤ min(x2) and the same for y. O(n), no sweep, no geometry.
20. “Two players stand on a grid. Report the number of lattice points strictly between them on the straight line joining them.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
gcd(|dx|, |dy|) − 1. Number theory wearing a geometry costume. Worth knowing because it is short and because it is the kind of fact that unlocks an otherwise-hard problem instantly.
21. “Given points, count triples that form a right angle at the middle point.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Dot product == 0, anchored at each candidate vertex. The dot product is the perpendicularity test the way the cross product is the collinearity test — the only place it earns its keep.
22. “A signal travels outward from each tower at 1 unit/sec. When is the whole city covered?”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Binary search on the answer, with a geometric feasibility check at each candidate time. ‘When does X first become true’ plus a monotone predicate is always binary search; the geometry is only inside the predicate.
23. “Find the maximum distance between any two of the n given points.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Read n first. n ≤ 5000 → brute force O(n²), and that is the intended answer far more often than people expect. Only at n ≥ 10⁵ does this become hull + rotating calipers — which is Tier D and essentially never asked.
24. “Count how many of the given segments are parallel to each other.”
Name the primitive and the supporting algorithm. Out loud is fine. Then unlock.
Canonical direction key, then k·(k−1)/2 per bucket. Same machine as r8. If the question is about distinct lines rather than directions, extend the key to the full reduced (A,B,C).
Under 90 seconds and correct on 20 of 24 is the bar. Below 16, do not move on to the timed sets — go back to the Toolkit tab for the sections you missed, because a recognition failure under no time pressure becomes a blank page under a clock.
Parts V & VI — the problem set
Twenty-eight problems. Every one exists; none are invented; none carry a fabricated company attribution. Where a problem is a Codeforces or Kattis exercise rather than a reported company question, it says so in its row and explains what it trains.
Mode 1 — guided. Work down the table in order while reading the matching Toolkit section. The concept column is visible; that is intentional at this stage.
Mode 2 — recognition. Two days later, come back, cover the concept and technique columns with your hand, and re-derive which primitive each one needs from the title alone.
Mode 3 — mixed. The Timed tab. No labels at all.
Mode 5 — repair. If you fail the same concept twice, the Reference tab maps the failure back to a section and a three-problem repair set.
| Problem | Difficulty | Geometry concept | Other techniques | OA relevance & evidence | Approach |
|---|---|---|---|---|---|
| LC 973 · K Closest Points to Origin | LC Medium | squared distance | heap, quickselect, sorting | Tier A — 10/25 companies, max freq 77 (Meta) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The single most company-tagged geometry problem in existence. Do it to prove to yourself that the geometry is one line and the problem is a selection problem. Sort or heap by |
| LC 1266 · Minimum Time Visiting All Points | LC Easy | Chebyshev distance | greedy | Tier A — 6/25 companies, freq 86 (Media.net) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The cleanest possible demonstration that diagonal movement means Chebyshev, not Euclidean and not Manhattan. Media.net tags it at 86 — and Media.net is on your list. With 8-directional movement, the time between two points is |
| LC 1041 · Robot Bounded In Circle | LC Medium | vectors, rotation | simulation | Tier B — widely tagged | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Disguised geometry with no coordinates in the statement. Trains the reflex of turning a described process into a vector. Simulate one pass through the instructions. The robot is bounded iff it ends at the origin, or it ends facing a direction other than north — because a net rotation means four passes return it to the start. The second condition is the insight; the first is obvious. |
| LC 1037 · Valid Boomerang | LC Easy | cross product | — | Tier A — Google, Microsoft | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Your Oracle OA, stripped to its core, in three points. Write it with cross and it is one line; write it with slopes and you will handle two special cases.
|
| LC 1232 · Check If It Is a Straight Line | LC Easy | collinearity | — | Tier A — Amazon | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The n-point version. Trains you to fix a reference pair once instead of comparing adjacent triples. Fix the first two points as the line. Check |
| LC 812 · Largest Triangle Area | LC Easy | signed area | brute force | Tier A — Google, Bloomberg, Amazon | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The area half of the cross product, and a direct test of whether you resist dividing by 2 too early. All O(n³) triples, take |
| CF 6A · Triangle | CF 900 | triangle inequality on lengths | brute force | Proxy — not an OA question | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Included because it is the lengths form of your Oracle problem, and doing both back to back is what fixes the ‘points → cross, lengths → inequality’ distinction. Four sticks, try all triples. Non-degenerate needs |
| LC 149 · Max Points on a Line | LC Hard | canonical slope | hash map | Tier A — 10/25 companies, max freq 56 (LinkedIn) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The second-most tagged geometry problem, and a 31% acceptance rate driven almost entirely by slope representation. If you do one problem from this file, do this one. For each anchor point i, hash every other point by |
| LC 447 · Number of Boomerangs | LC Medium | squared-distance buckets | hash map, combinatorics | Tier A — Google, Bloomberg | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Teaches the ‘anchor, then bucket by an exact key, then count ordered pairs’ pattern that runs through half the point-set problems. For each anchor, bucket the other points by |
| LC 593 · Valid Square | LC Medium | distance multiset | sorting | Tier A — TikTok, Google | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The Compute all 6 pairwise |
| LC 2013 · Detect Squares | LC Medium | point counting by diagonal | hash map, design | Tier A — Google, Amazon, Bloomberg, Meta | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: A design problem where the geometry is a filter. Four companies tag it, and the online-query shape is characteristic of modern OAs. Store counts in |
| LC 223 · Rectangle Area | LC Medium | rectangle overlap, inclusion–exclusion | — | Tier A — 6/25 companies, freq 52 (Nvidia) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The highest-frequency rectangle problem, and the only geometry problem in Microsoft’s recent six-month slice.
|
| LC 836 · Rectangle Overlap | LC Easy | interval overlap | — | Tier A — Microsoft, Amazon, Meta, Google | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Two characters of difficulty. Do it to burn the
|
| LC 939 · Minimum Area Rectangle | LC Medium | point set + rectangle | hash set | Tier A — Flipkart (53), Google, Microsoft, Meta | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The canonical ‘find a shape inside a point set’ problem, and a good lesson in choosing which pair to iterate over. Hash every point. For each pair with |
| LC 963 · Minimum Area Rectangle II | LC Medium | rotated rectangle | hash map, dot product | Tier B — Google (35) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Removes the axis-aligned crutch. The key insight is a property of rectangles that has nothing to do with their orientation. Two segments are the diagonals of a rectangle iff they share a midpoint and have equal length. Group every point pair by the key |
| LC 391 · Perfect Rectangle | LC Hard | corner parity + area | hash set, sweep | Tier B — Google, Amazon, Meta | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The best example in the set of replacing a hard geometric verification with two cheap invariants. Sum the areas; they must equal the bounding box’s area. Then toggle all four corners of every rectangle in a set — at the end exactly the four bounding-box corners must remain. Area alone permits a gap cancelled by an overlap; parity alone permits certain degenerate layouts. Together they are sufficient, and that is the thing to take away. |
| LC 56 · Merge Intervals | LC Medium | 1-D projection | sorting | Tier A — 21/25 companies, freq 93 (Zoho) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The most widely tagged problem in this entire file, geometry or not. You almost certainly can do it — do it anyway, from memory, and time yourself. It is the base case of every sweep. Sort by start. Keep a current interval; if the next start is ≤ the current end, extend the end, else emit and restart. The |
| LC 253 · Meeting Rooms II | LC Medium (premium) | event sweep | heap, sorting | Tier A — 17/25 companies, freq 84 (TikTok) | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The counter sweep, and the tie-break decision, in the form everyone asks for. If premium blocks you, LC 1851 · Minimum Interval to Include Each Query is the same algorithm with a public statement. Two ways, both worth writing. (a) Event sweep: +1 at start, −1 at end, sort with ends first, track the running max. (b) Min-heap of end times: for each meeting in start order, pop every meeting that has already finished, then push. Heap size is the answer. Here a meeting ending at 10 does not conflict with one starting at 10. |
| LC 218 · The Skyline Problem | LC Hard | sweep with a heap | heap, sorting | Tier B — 8/25 companies | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The hardest problem in the Tier A/B set and the one that most rewards having a clean event model. Eight companies tag it, including Google, Meta, Microsoft and Amazon. Events at every left and right edge. Sweep left to right keeping a multiset of active heights. When the maximum active height changes, emit a key point. The ordering rules at equal x are the entire difficulty: process all starts before all ends at the same x, sort starts by descending height and ends by ascending height. Write those three rules down before you code. |
| LC 850 · Rectangle Area II | LC Hard | sweep + coordinate compression | sorting | Tier B — Flipkart (60), Google | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The bridge from §5 to §7: many rectangles, one axis at a time. Compress all y-coordinates. Sweep x; between consecutive x-events compute the total covered y-length by scanning the compressed strips that are currently covered, and add |
| LC 1610 · Maximum Number of Visible Points | LC Hard | angular window | two pointers, sorting | Tier B — Nvidia (68), Google, Amazon | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The one problem that justifies learning
|
| CF 257C · View Angle | CF 1800 | angular sort | sorting | Proxy — trains LC 1610 | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Same machinery, inverted objective: find the smallest angle that contains all points. Doing it right after 1610 is what turns the technique from ‘a solution I saw’ into ‘a thing I can do’. Sort the angles. The answer is 360 minus the largest gap between consecutive angles (including the wrap-around gap from the last back to the first). One sort, one linear scan. |
| Kattis · Line Segment Intersection | Kattis | segment intersection | — | Proxy — trains §4 degeneracies | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The only exercise anywhere that forces every degenerate case, including printing an overlap segment when the answer is a segment rather than a point. There is no OA substitute for this. Full orientation quadruple plus the four |
| LC 335 · Self-Crossing | LC Hard | segment intersection, disguised | case analysis | Tier C — Bloomberg | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: A spiral walk that must decide whether it ever re-crosses itself. The statement contains the word ‘geometry’ nowhere. Two legitimate routes. (a) Keep all segments and run pairwise |
| LC 587 · Erect the Fence | LC Hard | convex hull | sorting | Tier C — Google, Amazon | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The only true convex-hull question in 25 company tag sets. One rep is the correct amount. Andrew’s monotone chain. This problem wants all boundary points including collinear ones, so use |
| Kattis · Convex Hull | Kattis | convex hull | — | Proxy — trains §9 | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: Exact feedback on the collinear convention, and multiple test cases including n = 1 and n = 2, which is where hull implementations actually break. Same function. Handle n < 3 before entering the loops. Kattis will tell you immediately if your collinear handling disagrees with theirs. |
| LC 1401 · Circle and Rectangle Overlapping | LC Medium | circle vs rectangle | — | Tier C — Google, Microsoft | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The clamp trick, which is the only circle technique the data justifies teaching. Clamp the circle centre into the rectangle on each axis independently: |
| LC 2101 · Detonate the Maximum Bombs | LC Medium | circle containment as a graph edge | DFS/BFS, graph | Tier B — Uber, Google, Amazon, Microsoft | Answer firstSolve it, or commit to an approach out loud, then unlock.
Why it is here: The archetype of what this whole research found: a one-line geometric predicate wrapped around an ordinary graph traversal. Directed edge i→j iff |
Counts, and why they are what they are
| Concept | Problems | Reasoning |
|---|---|---|
| Cross product family | 6 (g4–g7, g10, g25) | Tier A and it is the primitive everything else is built from. Repetition here pays into every other section. |
| Point-set hashing | 5 (g8–g11, g15) | Two of the ten most-tagged geometry problems live here, and the failure mode (representation, not algorithm) needs several reps to stick. |
| Rectangles | 5 (g12–g16) | The densest genuine-geometry cluster in the data. |
| Intervals and sweep | 4 (g17–g20) | Highest frequency of anything in the file. Four rather than eight only because you already own the base case. |
| Distance and metrics | 3 (g1–g3) | The idea is one line. Three problems is enough to make ‘never sqrt’ automatic. |
| Angular | 2 (g21, g22) | One technique, one real problem, one proxy to prove it transferred. |
| Segments | 2 (g23, g24) | Kattis forces the degeneracies better than any LeetCode problem; a third would be redundant. |
| Convex hull | 2 (g25, g26) | Two problems is what the evidence supports. Not four. |
| Circles | 2 (g27, g28) | Both reduce to dist2 vs r²; the second is really a graph problem, which is the lesson. |
Convex hull gets two problems and circles get two, against six for the cross product. That is not laziness — it is the coverage matrix applied honestly. Hull appears in two problems across twenty-five companies. Giving it six would mean taking four away from the cross product, which appears in dozens. If the evidence changes, the allocation should.
Part VIII & X — timed sets and the final assessment
Real problems, realistic weight, no topic labels and no hints. The point is not whether you know the techniques — by now you do — but whether you can find them under a clock, which is the thing that actually went wrong in the De Shaw OA and in the Oracle one.
- Close this file. All of it. No toolkit, no reference sheet, no editor autocomplete you would not have.
- Write the primitives from memory as part of the clock. If you would paste them in an OA you cannot, so do not practise that way.
- No brute-force stress testing during the clock — you will not have it. Afterwards, run
toolkit/stress.pyon anything you were unsure of. That habit is what turned CF 2264C around, and it belongs in the review, not the attempt. - Stop when the clock stops, even mid-thought. Score what you had. Knowing what your real 75-minute output looks like is what you came here to measure.
Set 1 — warm, but not easy
- LC 1266 · Minimum Time Visiting All Points
- LC 56 · Merge Intervals
- LC 939 · Minimum Area Rectangle
- LC 2101 · Detonate the Maximum Bombs
Two of these four are geometry by tag. Three are geometry in fact. Do not try to work out which — just solve them.
Set 2 — the hashing block
- LC 149 · Max Points on a Line
- LC 447 · Number of Boomerangs
- LC 253 · Meeting Rooms II
- LC 853 · Car Fleet
One of these is a stack problem that looks geometric. If you spend more than five minutes looking for a cross product in it, note that and move on.
Set 3 — full OA weight
- LC 223 · Rectangle Area
- LC 1610 · Maximum Number of Visible Points
- LC 11 · Container With Most Water
- LC 2013 · Detect Squares
This is the closest thing here to a real high-end OA: one easy, one hard with a subtle edge case, one classic, one design question.
Set 4 — the honest ceiling
- LC 218 · The Skyline Problem
- LC 587 · Erect the Fence
- LC 963 · Minimum Area Rectangle II
- LC 3025 · Find the Number of Ways to Place People I
Harder than most OAs will be. If you clear three of four inside 90 minutes, geometry is no longer a variable in your outcome.
Scoring — six axes, not one
“Solved / not solved” throws away the diagnosis. Score each problem out of 2 on each axis and write the numbers down; the pattern across four sets tells you what to repair.
| Axis | 0 | 1 | 2 |
|---|---|---|---|
| Recognition | never saw the structure | found it after a wrong start | named it in the first two minutes |
| Modelling | wrong reduction | right idea, wrong complexity | correct and appropriately sized for n |
| Implementation | did not compile in time | compiled, failed samples | clean first pass |
| Correctness | wrong answer | passed samples, failed hidden | accepted |
| Edge cases | did not consider any | considered, missed one | enumerated before coding |
| Time | over budget | at budget | under with time to re-check |
Recognition low, implementation high → the Recognition tab, not more problems. This is your documented pattern and the most likely result.
Recognition high, implementation low → you are typing too early. Enumerate the edge cases before the first line; this is the exact shape of the four bugs in your 2264C submission — the mathematics was right and the code was not.
Edge cases consistently 0–1 → the Edge cases tab, and run the battery against your own toolkit rather than reading it.
Time low everywhere else high → you are fine. Volume fixes this and nothing else does.
The standard, restated as a decision
Geometry has stopped being a variable in your OA outcome when, on Set 3 and Set 4 back to back on different days, you clear three of four inside the clock with recognition scored 2 on at least three problems per set. Anything less is not failure — it is a pointer to a section.
Part IX — how geometry solutions actually fail
Naive geometry code does not fail randomly. It fails in about a dozen specific ways, and every one of them has a two-line test case. Meeting them here is much cheaper than meeting them in the hidden tests.
The degenerate battery
Every one of these should be a test you run against your own from-memory toolkit. They are all in
toolkit/selftest.cpp, which fails loudly and tells you which case broke.
| Case | Why it breaks things | What must happen |
|---|---|---|
| Two coincident points | zero-length direction vector; gcd(0,0) = 0 then a division by it | cross returns 0, onSegment is still correct, slopeKey guards the gcd |
| Three identical points | every derived quantity is zero at once | rejected as a triangle; not counted as a line of three points unless the statement says so |
| Zero-length segment | it is a point wearing a segment’s type | segIntersect handles it through the onSegment branch with no special case |
| Three collinear points | the ‘triangle’ has zero area | cross == 0, rejected |
| Collinear overlapping segments | all four orientations are 0, so the proper test says no | true, via onSegment |
| Collinear disjoint segments | all four orientations are also 0 | false — this is why ‘all collinear → true’ is wrong |
| Segments touching at one endpoint | one orientation is 0, killing d1*d2 < 0 | true, via onSegment |
| Perfectly vertical line | dy/dx divides by zero | slopeKey returns (0, 1) |
| Perfectly horizontal line | sign normalisation can flip it inconsistently | slopeKey returns (1, 0) |
| All-negative coordinates | abs forgotten; sign convention inverted | identical results to the translated-positive case |
| Coordinates at the constraint maximum | the overflow you did not run the ritual on | no overflow, verified by the battery at 109 |
| Point exactly on a triangle edge or at a vertex | strict-vs-inclusive ambiguity | pointInTriangle returns 0, and you decide what 0 means for this statement |
| Rectangles sharing exactly one edge or corner | < vs ≤ | matches the convention you chose, deliberately |
| Polygon with repeated or collinear vertices | shoelace is fine; hull and containment are not | shoelace exact; hull de-duplicates first |
| Points coincident with the observer (angular) | atan2(0,0) silently returns 0 | counted separately and added to every window |
Six wrong implementations, and the fix
1. The overflow that the return type does not fix
long long cross(P a, P b, P c) { // x, y are int
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
Every operation happens in int; the widening occurs after the damage.
Fix: make the members long long. Not a cast at the call site — a cast you
must remember is a cast you will forget.
2. The double slope
double slope = (double)(b.y - a.y) / (b.x - a.x);
mp[slope]++; // LC 149, wrong answer
auto g = std::gcd(llabs(dx), llabs(dy));
if (g) { dx /= g; dy /= g; }
if (dx < 0 || (dx == 0 && dy < 0)) { dx = -dx; dy = -dy; }
mp[{dx, dy}]++; // exact
3. The collinear blindness in segment intersection
bool segIntersect(P a, P b, P c, P d) {
return orient(a,b,c) * orient(a,b,d) < 0
&& orient(c,d,a) * orient(c,d,b) < 0; // misses every touching and overlapping case
}
This is right for the generic case and wrong for four of the six configurations in the diagram back
in §4. The four onSegment lines are not defensive coding; they are four distinct
verdicts.
4. The triangle test that assumes counter-clockwise
bool inside(P a, P b, P c, P p) {
return orient(a,b,p) > 0 && orient(b,c,p) > 0 && orient(c,a,p) > 0;
}
Correct for a counter-clockwise triangle, silently always-false for a clockwise one. Compare the three signs to each other, as in §3, or normalise the winding first.
5. The sweep whose tie-break was never chosen
sort(ev.begin(), ev.end()); // and no comment anywhere about what happens at equal x
Not wrong, just undecided — which works on the samples and then gets flipped back the first time one seems to disagree. Write the convention in a comment when you write the sort.
6. The epsilon pointing the wrong way
while (a[r] - a[l] > angle - 1e-9) l++; // strict where the statement was permissive
LC 1610 says a point exactly on the edge of the field of view is visible, so the tolerance
must widen the window, not narrow it: > angle + 1e-9. Read the boundary sentence, then
point the epsilon at it deliberately.
Stress-test the degeneracies
You already proved this works: the stress-test habit is what turned CF 2264C from three-plus submissions into one accepted on the first try. Geometry is the best subject for it, because a correct-but-slow reference is usually trivial — O(n³) over all triples, or a direct simulation — and the degenerate inputs that break geometry are exactly the ones a random generator produces when you restrict coordinates to a small range.
toolkit/stress.py is the harness. Generate coordinates in −3..3. Small ranges force collinearity, coincidence and tangency to appear within a few hundred cases; large random coordinates almost never produce a degenerate configuration, which is why stress-testing with big numbers finds nothing and feels reassuring.
# find a counterexample, smallest coordinates first
python toolkit/stress.py --fast ./sol --slow ./brute --gen ./gen.py --range 3 --n 6
Part XI — the sheet you should know by heart
Everything below should be reproducible from memory in under six minutes by the end of Week 1. Type it at the start of every session until that is true. It is about sixty lines and it is the entire toolkit.
using ll = long long;
struct P {
ll x, y; // ll HERE, not int
bool operator<(const P& o) const { return x != o.x ? x < o.x : y < o.y; }
bool operator==(const P& o) const { return x == o.x && y == o.y; }
};
ll cross(P a, P b, P c) { // (b-a) x (c-a)
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
int orient(P a, P b, P c) { ll v = cross(a,b,c); return (v>0) - (v<0); }
ll dot (P a, P b, P c) { return (b.x-a.x)*(c.x-a.x) + (b.y-a.y)*(c.y-a.y); }
ll dist2(P a, P b) { ll dx=a.x-b.x, dy=a.y-b.y; return dx*dx + dy*dy; }
bool onSegment(P a, P b, P p) { // p on segment ab, inclusive
if (cross(a,b,p) != 0) return false;
return min(a.x,b.x) <= p.x && p.x <= max(a.x,b.x)
&& min(a.y,b.y) <= p.y && p.y <= max(a.y,b.y);
}
bool segIntersect(P a, P b, P c, P d) {
int d1=orient(a,b,c), d2=orient(a,b,d), d3=orient(c,d,a), d4=orient(c,d,b);
if (d1*d2 < 0 && d3*d4 < 0) return true;
if (d1==0 && onSegment(a,b,c)) return true;
if (d2==0 && onSegment(a,b,d)) return true;
if (d3==0 && onSegment(c,d,a)) return true;
if (d4==0 && onSegment(c,d,b)) return true;
return false;
}
int pointInTriangle(P a, P b, P c, P p) { // 1 in, 0 boundary, -1 out
int d1=orient(a,b,p), d2=orient(b,c,p), d3=orient(c,a,p);
bool neg = d1<0 || d2<0 || d3<0, pos = d1>0 || d2>0 || d3>0;
if (neg && pos) return -1;
if (!d1 || !d2 || !d3) return 0;
return 1;
}
ll twicePolygonArea(const vector<P>& v) { // signed: + is CCW
ll s = 0; int n = v.size();
for (int i = 0; i < n; i++) {
P a = v[i], b = v[(i+1)%n];
s += a.x*b.y - a.y*b.x;
}
return s;
}
pair<ll,ll> slopeKey(P a, P b) {
ll dx = b.x-a.x, dy = b.y-a.y, g = std::gcd(llabs(dx), llabs(dy));
if (g) { dx /= g; dy /= g; }
if (dx < 0 || (dx == 0 && dy < 0)) { dx = -dx; dy = -dy; }
return {dx, dy};
}
struct R { ll x1, y1, x2, y2; };
bool overlapArea(R a, R b) { // touching does NOT count
return max(a.x1,b.x1) < min(a.x2,b.x2) && max(a.y1,b.y1) < min(a.y2,b.y2);
}
ll overlapAreaValue(R a, R b) {
ll w = min(a.x2,b.x2)-max(a.x1,b.x1), h = min(a.y2,b.y2)-max(a.y1,b.y1);
return (w>0 && h>0) ? w*h : 0;
}
vector<P> convexHull(vector<P> p) {
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
if (p.size() < 3) return p;
vector<P> h;
for (int pass = 0; pass < 2; pass++) {
size_t st = h.size();
for (P q : p) {
while (h.size() >= st+2 && cross(h[h.size()-2], h[h.size()-1], q) <= 0)
h.pop_back(); // <= drops collinear, < keeps them
h.push_back(q);
}
h.pop_back();
reverse(p.begin(), p.end());
}
return h;
}
The decision table — what to reach for
| You see… | Reach for | § |
|---|---|---|
| “valid triangle” from points | cross != 0 | 3 |
| “valid triangle” from lengths | sort + triangle inequality | 3 |
| “same line”, “collinear”, “straight” | cross == 0 or slopeKey | 2, 6 |
| “closest”, “within distance r” | dist2 vs r*r | 1 |
| “diagonal movement allowed” | Chebyshev max(|dx|,|dy|) | 1 |
| “4-directional”, “city blocks” | Manhattan; solve each axis separately | 1 |
| overlapping rectangles, 2 of them | interval overlap on both axes | 5 |
| overlapping rectangles, many | sweep + coordinate compression | 7 |
| start/end times, meetings, jobs | event sweep with a counter | 7 |
| “field of view”, “within an angle” | atan2 + doubled window | 8 |
| “enclose all”, “minimum fence” | convex hull | 9 |
| “area of the region” | shoelace | 10 |
| “inside the plot / region” | point in polygon | 10 |
| a radius creating a relation between objects | dist2 ≤ r² as a graph edge | 11 |
| “maximum distance” and n ≤ 5000 | brute force O(n²) — genuinely | 9 |
Repair table — if you failed the standard
| What went wrong | It is not | Go to | Do these three |
|---|---|---|---|
| Did not see the geometry at all | a knowledge gap | Recognition tab, all 24 drills | g4, g5, g28 |
| Saw it, picked the wrong primitive | a recognition gap — a mapping gap | The decision table above | g7, g10, g14 |
| Right primitive, wrong answer | conceptual | Edge cases tab + selftest.cpp | g13, g23, g21 |
| Right answer, too slow | geometry at all | Re-read n in the constraint line first | g6, g24, g20 |
| Overflowed | bad luck | The numeric ritual, §0 | g8, g12, g16 |
| Could not write the toolkit from memory | a reason to re-read | Type it daily for a week | the sheet above, six minutes, timed |
Retention is your documented failure mode, not comprehension — most of those 109 hards were looked up and are not retrievable today. So:
Day 1 after finishing Week 1: type the sheet from memory, no reference. Diff it against toolkit/geo.hpp. Day 3: again, plus one Tier A problem cold. Day 10: again, plus Timed Set 2. Then two problems a week, forever, from memory, no reference.
Thirty minutes a week is what keeps twenty hours from evaporating. Skipping it is the only way this file becomes another unopened booklet.