E - 凸図形の合成 / Composition of Convex Shapes Editorial by admin
Gemini 3.0 Flash (Thinking)Overview
Given \(N\) convex shapes (points, line segments, convex polygons), we need to find the number of ways to select 1 or more of them such that their Minkowski sum becomes a “convex polygon with positive area.”
By leveraging the properties of Minkowski sums, we can identify the conditions under which the area becomes \(0\) (i.e., the result is a point or a line segment). This allows us to solve the problem using the “complementary counting” approach — subtracting the invalid combinations from the total.
Analysis
1. Properties of Minkowski Sums and Area
The Minkowski sum \(A \oplus B\) of convex shapes \(A, B\) is again a convex shape. The area of this sum becomes \(0\) (i.e., does not form a convex polygon) only in the following cases:
- Case A: All selected shapes are “points.”
- The sum of points is a point.
- Case B: The selected shapes consist only of “points” and “line segments all pointing in the same direction.”
- The sum of parallel line segments results in a (longer) line segment with the same slope. Adding points to this only shifts the position without changing the shape from a line segment.
Conversely, if at least one of the following conditions is met, the Minkowski sum is guaranteed to be a “convex polygon” with positive area: - At least one “convex polygon (a shape with positive area)” is selected. - Two or more “line segments” with different directions (slopes) are selected.
2. Counting via Complementary Approach
Since directly counting “combinations with positive area” is difficult, we compute as follows: 1. Total combinations: Find the total number of non-empty subsets \(2^N - 1\). 2. Combinations with area 0: Count the cases from Case A and Case B above, and subtract them.
3. Classification of Shapes
Each given shape \(P_i\) is classified into one of the following 3 types: - Polygon: A shape with positive area. Including one immediately guarantees positive area. - Point: A shape with no direction. - Line segment: A shape with a specific direction (slope).
Note that when \(K_i \geq 3\) but all vertices are collinear, the shape must be treated as a “line segment.”
Algorithm
Organizing the shapes:
- \(N_{pts}\): Number of points
- \(directions\): A hash map managing the number of line segments for each “direction (slope)”
- For polygons, it suffices to just track their count (since they are included in the total \(2^N\) combinations).
Direction normalization:
- Given the endpoints of a line segment \((x_1, y_1), (x_2, y_2)\), compute the vector \((dx, dy) = (x_2-x_1, y_2-y_1)\).
- Divide by \(g = \gcd(|dx|, |dy|)\), then further normalize to the “upper half (\(dx > 0\), or \(dx=0, dy>0\))” range, so that line segments with the same slope are identified as equivalent.
Formula: The number of non-empty subsets with area \(0\) can be expressed using an inclusion-exclusion-like approach:
- For each “direction \(d\)”, add up (the number of non-empty subsets consisting only of points and line segments with direction \(d\)).
- However, this overcounts “subsets consisting only of points,” so an adjustment is needed.
Formally, the number of subsets with area \(0\) is: $\(\left( \sum_{d \in \text{Directions}} (2^{N_{pts} + C_d} - 1) \right) - (M - 1)(2^{N_{pts}} - 1)\)\( where \)Cd\( is the number of line segments with direction \)d\(, and \)M\( is the total number of distinct directions. The second term accounts for the fact that "point-only subsets (\)2^{N{pts}}-1\( possibilities)" are counted \)M\( times across each direction \)d\(, so we subtract the \)M-1$ redundant counts.
- Final answer:
$\(\text{Ans} = (2^N - 1) - \text{(number with area 0)} \pmod{10^9+7}\)$
Simplifying this yields the formula used in the code:
pow2[N] - sum(2^{N_pts + C_d}) + (M - 1) * 2^{N_pts}.
Complexity
- Time complexity: \(O(\sum K_i + N \log (\max |X|))\)
- Scanning the vertices of each shape takes \(O(\sum K_i)\).
- GCD computation for determining line segment directions takes \(O(N \log (\max |X|))\).
- Hash map operations take \(O(N)\) on average.
- Space complexity: \(O(\sum K_i)\)
- Depends on storing vertex information and the direction counting map.
Implementation Notes
Collinearity check: When \(K_i \geq 3\), you can determine whether the shape is a polygon or a line segment by checking whether the cross product of the first 3 points is \(0\).
Large coordinates: Since coordinates can be as large as \(10^9\), do not use floating-point numbers (
double) for managing slopes. Instead, use GCD-normalized integer pairs(dx, dy)as dictionary keys.Avoiding division by zero: The case \(dx=0\) is naturally handled by the GCD-based normalization.
Source Code
import sys
import math
# Use a generator to read input values one by one to save memory
def get_input():
for line in sys.stdin:
for word in line.split():
yield word
def get_direction(x1, y1, x2, y2):
"""
Calculate the normalized direction vector of a line segment.
The direction is represented by (dx, dy) where gcd(|dx|, |dy|) = 1
and the vector is in the upper half-plane (dx > 0 or (dx == 0 and dy > 0)).
"""
dx = x2 - x1
dy = y2 - y1
# Since vertices are distinct, dx and dy are not both zero.
g = math.gcd(abs(dx), abs(dy))
dx //= g
dy //= g
# Normalize the direction vector
if dx < 0 or (dx == 0 and dy < 0):
dx = -dx
dy = -dy
return (dx, dy)
def solve():
input_gen = get_input()
try:
line = next(input_gen)
except StopIteration:
return
N = int(line)
N_pts = 0
directions = {}
for _ in range(N):
try:
K_str = next(input_gen)
except StopIteration:
break
K = int(K_str)
if K == 1:
# The figure is a point
next(input_gen) # x coordinate
next(input_gen) # y coordinate
N_pts += 1
elif K == 2:
# The figure is a line segment
x1 = int(next(input_gen))
y1 = int(next(input_gen))
x2 = int(next(input_gen))
y2 = int(next(input_gen))
d = get_direction(x1, y1, x2, y2)
directions[d] = directions.get(d, 0) + 1
else:
# K >= 3: The figure is either a convex polygon or a line segment
x1 = int(next(input_gen))
y1 = int(next(input_gen))
x2 = int(next(input_gen))
y2 = int(next(input_gen))
x3 = int(next(input_gen))
y3 = int(next(input_gen))
# Check for collinearity using the cross product of (x2-x1, y2-y1) and (x3-x2, y3-y2).
# If the cross product is zero, the first three points are collinear.
# Due to the "no three consecutive vertices are collinear" rule for polygons,
# this implies all vertices must be collinear if the first three are.
is_collinear = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2) == 0
last_x, last_y = x3, y3
for _ in range(3, K):
last_x = int(next(input_gen))
last_y = int(next(input_gen))
if is_collinear:
# All vertices are collinear, so it's a line segment.
# The endpoints are the first and the last given vertices.
d = get_direction(x1, y1, last_x, last_y)
directions[d] = directions.get(d, 0) + 1
else:
# Not collinear, so it's a convex polygon (area > 0).
pass
MOD = 10**9 + 7
# Precompute powers of 2 up to N modulo 10^9 + 7
pow2 = [1] * (N + 1)
for i in range(1, N + 1):
pow2[i] = (pow2[i-1] * 2) % MOD
# The Minkowski sum of a subset of convex shapes has area 0 if and only if
# all shapes in the subset are points or line segments that are parallel to each other.
# Otherwise, the Minkowski sum is a convex polygon (area > 0).
num_dirs = len(directions)
# Total number of non-empty subsets is 2^N - 1.
# We subtract the number of non-empty subsets that result in area 0.
# Non-empty subsets with area 0 are:
# 1. Non-empty subsets consisting only of points.
# 2. For each direction d, subsets consisting of points and at least one line segment
# of direction d.
# Using inclusion-exclusion or partition logic:
# Area-zero subsets (including empty) = sum_d 2^(N_pts + C_d) - (num_dirs - 1) * 2^N_pts
# Number of area > 0 subsets = (2^N - 1) - (Area-zero subsets - 1)
# Area > 0 subsets = 2^N - sum_d 2^(N_pts + C_d) + (num_dirs - 1) * 2^N_pts
ans = pow2[N]
sum_term = 0
for d in directions:
C_d = directions[d]
sum_term = (sum_term + pow2[N_pts + C_d]) % MOD
ans = (ans - sum_term) % MOD
ans = (ans + (num_dirs - 1) * pow2[N_pts]) % MOD
# Final answer modulo 10^9 + 7
print(ans % MOD)
if __name__ == "__main__":
solve()
This editorial was generated by gemini-3-flash-thinking.