|
| 1 | +# [Problem 3625: Count Number of Trapezoids II](https://leetcode.com/problems/count-number-of-trapezoids-ii/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +I need to count convex quadrilaterals with at least one pair of parallel sides among given points. A trapezoid requires two opposite sides to be parallel. If I fix an orientation (slope), then consider all lines with that slope that contain points: choosing two distinct lines and picking two points from each line (two on line A and two on line B) gives a quadrilateral whose the two chosen sides (joining points on the same line) are parallel. So for each slope, if for each parallel line i there are k_i points, the number of ways to pick two points on two different parallel lines is sum_{i<j} C(k_i,2) * C(k_j,2). Summing over slopes counts all quadrilaterals that have a pair of parallel sides in that orientation. |
| 5 | + |
| 6 | +But parallelograms have two pairs of parallel sides, so they will be counted twice (once for each orientation). So subtract parallelograms once. Parallelograms can be counted by the standard midpoint trick: diagonals of a parallelogram share the same midpoint — count unordered pairs of point-pairs (i,j) that share the same midpoint: for each midpoint, C(cnt,2) parallelograms. |
| 7 | + |
| 8 | +Implementation detail: we don't need k_i explicitly. If we iterate over all unordered pairs of points (i,j) and group them by the normalized slope and the line offset (perpendicular dot), then for a fixed (slope, offset) the number of such pairs equals C(k_i,2). So we can accumulate counts per (slope, offset) directly. Complexity is O(n^2) pairs which is OK for n <= 500. |
| 9 | + |
| 10 | +## Refining the problem, round 2 thoughts |
| 11 | +- Represent slope as a normalized direction vector (dx/g, dy/g) with a consistent sign convention (dx>0 or dx==0 and dy>0) to group parallel directions. |
| 12 | +- For a given normalized direction vector v = (dxn, dyn), a consistent perpendicular vector is p = (-dyn, dxn). For a point (x,y) the value offset = p.x * x + p.y * y uniquely identifies which parallel line (with that direction) the point lies on. When building pairs (i,j) we can use offset computed from one endpoint; offset is the same for both endpoints on that line. |
| 13 | +- When iterating pairs, increment slope_map[slope][offset] by 1 (this count equals C(k_i,2) for that line). |
| 14 | +- For each slope, compute S = sum c_i and sum_sq = sum c_i^2 and number of trapezoids for that slope is (S^2 - sum_sq)//2 (which equals sum_{i<j} c_i * c_j). |
| 15 | +- Compute parallelograms by mapping midpoints (xi+xj, yi+yj) (doubled coordinates to stay integers) to counts and summing C(cnt,2). |
| 16 | +- Final answer = total_trapezoids_by_slope_sum - parallelograms. |
| 17 | + |
| 18 | +Edge cases: |
| 19 | +- Vertical/horizontal/slopes of any sign handled by normalization. |
| 20 | +- All points distinct is guaranteed. |
| 21 | +- All computations are integer; use gcd for normalization. |
| 22 | + |
| 23 | +Time complexity: O(n^2) to enumerate pairs and build maps, and O(#distinct_slope * #lines_per_slope) roughly bounded by O(n^2) still. Space complexity: O(n^2) in worst-case for maps. |
| 24 | + |
| 25 | +## Attempted solution(s) |
| 26 | +```python |
| 27 | +from collections import defaultdict |
| 28 | +from math import gcd |
| 29 | +from typing import List |
| 30 | + |
| 31 | +class Solution: |
| 32 | + def countTrapezoids(self, points: List[List[int]]) -> int: |
| 33 | + n = len(points) |
| 34 | + slope_map = defaultdict(lambda: defaultdict(int)) # slope -> (offset -> count_of_pairs_on_that_line) |
| 35 | + mid_map = defaultdict(int) # midpoint (2x,2y) -> count_of_pairs_that_have_this_midpoint |
| 36 | + |
| 37 | + for i in range(n): |
| 38 | + xi, yi = points[i] |
| 39 | + for j in range(i+1, n): |
| 40 | + xj, yj = points[j] |
| 41 | + dx = xj - xi |
| 42 | + dy = yj - yi |
| 43 | + g = gcd(dx, dy) |
| 44 | + dxn = dx // g |
| 45 | + dyn = dy // g |
| 46 | + # normalize sign so direction is unique |
| 47 | + if dxn < 0 or (dxn == 0 and dyn < 0): |
| 48 | + dxn = -dxn |
| 49 | + dyn = -dyn |
| 50 | + slope = (dxn, dyn) |
| 51 | + # perpendicular vector to the direction (dxn, dyn) |
| 52 | + perp_x, perp_y = -dyn, dxn |
| 53 | + offset = perp_x * xi + perp_y * yi # integer identifier for the parallel line |
| 54 | + slope_map[slope][offset] += 1 |
| 55 | + |
| 56 | + # midpoint key (double coordinates to stay integral) |
| 57 | + mid_key = (xi + xj, yi + yj) |
| 58 | + mid_map[mid_key] += 1 |
| 59 | + |
| 60 | + total_by_slope = 0 |
| 61 | + for slope, offmap in slope_map.items(): |
| 62 | + # offmap[offset] is the number of unordered pairs of points on that particular line, |
| 63 | + # which equals C(k,2) where k is number of points on the line. |
| 64 | + vals = list(offmap.values()) |
| 65 | + S = sum(vals) |
| 66 | + sum_sq = sum(v * v for v in vals) |
| 67 | + total_by_slope += (S * S - sum_sq) // 2 # sum_{i<j} c_i * c_j |
| 68 | + |
| 69 | + # parallelograms counted by midpoint collisions: for each midpoint value with cnt pairs, |
| 70 | + # number of parallelograms is C(cnt,2) |
| 71 | + parallelograms = sum(cnt * (cnt - 1) // 2 for cnt in mid_map.values()) |
| 72 | + |
| 73 | + return total_by_slope - parallelograms |
| 74 | +``` |
| 75 | +- Notes: |
| 76 | + - We iterate over all unordered pairs of points (i<j). For each pair we: |
| 77 | + - add 1 to the count of pairs on the particular line identified by (slope, offset). That count equals C(k,2) for that line (k = #points on the line). |
| 78 | + - record the midpoint (xi + xj, yi + yj) to count diagonal-pair collisions (parallelograms). |
| 79 | + - For each slope, if the lines with that slope have pair-counts c_i = C(k_i,2), the number of trapezoids whose parallel sides have that slope is sum_{i<j} c_i * c_j = (S^2 - sum c_i^2) // 2. |
| 80 | + - Parallelograms are counted twice across slopes (once per pair of parallel sides), so subtract the parallelogram count once. |
| 81 | + - Time complexity: O(n^2) to process all pairs; space complexity O(n^2) worst-case for maps. This fits constraints (n <= 500). |
0 commit comments