1- use crate :: util:: grid:: * ;
2- use crate :: util:: hash:: * ;
31use crate :: util:: iter:: * ;
42use crate :: util:: parse:: * ;
5- use crate :: util:: point:: * ;
6- use std:: collections:: VecDeque ;
7-
8- const OUTSIDE : i64 = 0 ;
9- const INSIDE : i64 = 1 ;
10- const UNKNOWN : i64 = 2 ;
113
124type Tile = [ u64 ; 2 ] ;
135
@@ -30,75 +22,148 @@ pub fn part1(tiles: &[Tile]) -> u64 {
3022}
3123
3224pub fn part2 ( tiles : & [ Tile ] ) -> u64 {
33- let size = tiles. len ( ) ;
34- let shrink_x = shrink ( tiles, 0 ) ;
35- let shrink_y = shrink ( tiles, 1 ) ;
36- let shrunk: Vec < _ > = tiles. iter ( ) . map ( |& [ x, y] | ( shrink_x[ & x] , shrink_y[ & y] ) ) . collect ( ) ;
25+ let mut tiles = tiles. to_vec ( ) ;
3726
38- let mut area = 0 ;
39- let mut todo = VecDeque :: from ( [ ORIGIN ] ) ;
40- let mut grid = Grid :: new ( shrink_x. len ( ) as i32 , shrink_y. len ( ) as i32 , UNKNOWN ) ;
27+ tiles. sort_unstable_by_key ( |& [ x, y] | ( y, x) ) ;
28+
29+ let tiles = tiles;
30+
31+ // Track the largest area so far during scanning:
32+ let mut largest_area: u64 = 0 ;
4133
42- for i in 0 ..size {
43- let ( x1, y1, x2, y2) = minmax ( shrunk[ i] , shrunk[ ( i + 1 ) % size] ) ;
34+ // Each red tile (`x`, `y`) becomes a candidate for being a top corner of the largest area, and during the
35+ // scan the `interval` containing the maximum possible width is updated:
36+ struct Candidate {
37+ x : u64 ,
38+ y : u64 ,
39+ interval : Interval ,
40+ }
41+
42+ let mut candidates: Vec < Candidate > = Vec :: with_capacity ( 512 ) ;
43+
44+ // Maintain an ordered list of descending edges, i.e. [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...]:
45+ let mut descending_edges: Vec < u64 > = vec ! [ ] ;
46+ let mut intervals_from_descending_edges = vec ! [ ] ;
47+
48+ // Invariants on the input data (defined by the puzzle) result in points arriving in pairs on the same y line:
49+ let mut it = tiles. into_iter ( ) ;
50+
51+ while let ( Some ( [ x0, y] ) , Some ( [ x1, y1] ) ) = ( it. next ( ) , it. next ( ) ) {
52+ debug_assert_eq ! ( y, y1) ;
53+
54+ // Update the descending edges; since we are scanning from top to bottom, and within each line left to right,
55+ // when we, starting from outside of the region, hit a corner tile it is either:
56+ //
57+ // - The corner of two edges, one going right and one going down. In this case, the `descending_edges` won't contain
58+ // the `x` coordinate, and we should "toggle" it on to denote that there is a new descending edge.
59+ // - The corner of two edges, one going right and one going up. The `descending_edges` will contain an `x` coordinate,
60+ // that should be "toggled" off.
61+ //
62+ // Simular arguments work for when we are scanning inside the edge and we hit the corner that ends the edge; this is also
63+ // why corners always arrive in pairs.
64+ //
65+ // Do the update:
66+ for x in [ x0, x1] {
67+ toggle_value_membership_in_ordered_list ( & mut descending_edges, x) ;
68+ }
4469
45- for x in x1..x2 + 1 {
46- for y in y1..y2 + 1 {
47- grid[ Point :: new ( x, y) ] = INSIDE ;
70+ // Every pair of descending edges in the ordered list defines a region; find the resulting intervals on this line:
71+ update_intervals_from_descending_edges (
72+ & descending_edges,
73+ & mut intervals_from_descending_edges,
74+ ) ;
75+
76+ // Check the rectangles this red tile could be a bottom tile for, with the current candidates:
77+ for candidate in candidates. iter ( ) {
78+ for x in [ x0, x1] {
79+ if candidate. interval . contains ( x) {
80+ largest_area = largest_area
81+ . max ( ( candidate. x . abs_diff ( x) + 1 ) * ( candidate. y . abs_diff ( y) + 1 ) ) ;
82+ }
4883 }
4984 }
50- }
5185
52- while let Some ( point) = todo. pop_front ( ) {
53- for next in ORTHOGONAL . map ( |o| point + o) {
54- if grid. contains ( next) && grid[ next] == UNKNOWN {
55- grid[ next] = OUTSIDE ;
56- todo. push_back ( next) ;
86+ // Update candidates when their interval shrinks due to descending edge changes, and drop them when their interval becomes empty:
87+ candidates. retain_mut ( |candidate| {
88+ if let Some ( intersection_containing_x) =
89+ intervals_from_descending_edges. iter ( ) . find ( |i| i. contains ( candidate. x ) )
90+ {
91+ candidate. interval = intersection_containing_x. intersection ( candidate. interval ) ;
92+
93+ true
94+ } else {
95+ false
96+ }
97+ } ) ;
98+
99+ // Add any new candidates:
100+ for x in [ x0, x1] {
101+ if let Some ( & containing) =
102+ intervals_from_descending_edges. iter ( ) . find ( |i| i. contains ( x) )
103+ {
104+ candidates. push ( Candidate { x, y, interval : containing } ) ;
57105 }
58106 }
59107 }
60108
61- for y in 1 ..grid. height {
62- for x in 1 ..grid. width {
63- let point = Point :: new ( x, y) ;
64- let value = i64:: from ( grid[ point] != OUTSIDE ) ;
65- grid[ point] = value + grid[ point + UP ] + grid[ point + LEFT ] - grid[ point + UP + LEFT ] ;
66- }
109+ largest_area
110+ }
111+
112+ /// The set { x in u64 | l <= x <= r }.
113+ #[ derive( Clone , Copy ) ]
114+ struct Interval {
115+ l : u64 ,
116+ r : u64 ,
117+ }
118+
119+ impl Interval {
120+ fn new ( l : u64 , r : u64 ) -> Self {
121+ debug_assert ! ( l <= r) ;
122+
123+ Interval { l, r }
67124 }
68125
69- for i in 0 ..size {
70- for j in i + 1 ..size {
71- let ( x1, y1, x2, y2) = minmax ( shrunk[ i] , shrunk[ j] ) ;
72-
73- let expected = ( x2 - x1 + 1 ) as i64 * ( y2 - y1 + 1 ) as i64 ;
74- let actual = grid[ Point :: new ( x2, y2) ]
75- - grid[ Point :: new ( x1 - 1 , y2) ]
76- - grid[ Point :: new ( x2, y1 - 1 ) ]
77- + grid[ Point :: new ( x1 - 1 , y1 - 1 ) ] ;
78-
79- if expected == actual {
80- let [ x1, y1] = tiles[ i] ;
81- let [ x2, y2] = tiles[ j] ;
82- let dx = x1. abs_diff ( x2) + 1 ;
83- let dy = y1. abs_diff ( y2) + 1 ;
84- area = area. max ( dx * dy) ;
85- }
86- }
126+ fn intersects ( self , other : Self ) -> bool {
127+ other. l <= self . r && self . l <= other. r
87128 }
88129
89- area
130+ fn intersection ( self , other : Self ) -> Self {
131+ debug_assert ! ( self . intersects( other) ) ;
132+
133+ Interval :: new ( self . l . max ( other. l ) , self . r . min ( other. r ) )
134+ }
135+
136+ fn contains ( self , x : u64 ) -> bool {
137+ self . l <= x && x <= self . r
138+ }
90139}
91140
92- fn shrink ( tiles : & [ Tile ] , index : usize ) -> FastMap < u64 , i32 > {
93- let mut axis: Vec < _ > = tiles. iter ( ) . map ( |tile| tile[ index] ) . collect ( ) ;
94- axis. push ( u64:: MIN ) ;
95- axis. push ( u64:: MAX ) ;
96- axis. sort_unstable ( ) ;
97- axis. dedup ( ) ;
98- axis. iter ( ) . enumerate ( ) . map ( |( i, & n) | ( n, i as i32 ) ) . collect ( )
141+ // Adds `value` if it isn't in `ordered_list`, removes it if it is, maintaining the order.
142+ fn toggle_value_membership_in_ordered_list ( ordered_list : & mut Vec < u64 > , value : u64 ) {
143+ let mut i = 0 ;
144+
145+ while i < ordered_list. len ( ) && ordered_list[ i] < value {
146+ i += 1 ;
147+ }
148+
149+ if i == ordered_list. len ( ) || ordered_list[ i] != value {
150+ ordered_list. insert ( i, value) ;
151+ } else {
152+ ordered_list. remove ( i) ;
153+ }
99154}
100155
156+ // Changes the list of descending edges, [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...],
157+ // into a vector containing the intervals.
101158#[ inline]
102- fn minmax ( ( x1, y1) : ( i32 , i32 ) , ( x2, y2) : ( i32 , i32 ) ) -> ( i32 , i32 , i32 , i32 ) {
103- ( x1. min ( x2) , y1. min ( y2) , x1. max ( x2) , y1. max ( y2) )
159+ fn update_intervals_from_descending_edges ( descending_edges : & [ u64 ] , to_update : & mut Vec < Interval > ) {
160+ debug_assert ! ( descending_edges. len( ) . is_multiple_of( 2 ) ) ;
161+
162+ to_update. clear ( ) ;
163+
164+ let mut it = descending_edges. iter ( ) ;
165+
166+ while let ( Some ( & l) , Some ( & r) ) = ( it. next ( ) , it. next ( ) ) {
167+ to_update. push ( Interval :: new ( l, r) ) ;
168+ }
104169}
0 commit comments