|
1 | 1 | package g3401_3500.s3454_separate_squares_ii; |
2 | 2 |
|
3 | | -// #Hard #2025_02_16_Time_246_ms_(100.00%)_Space_79.98_MB_(100.00%) |
| 3 | +// #Hard #Array #Binary_Search #Segment_Tree #Line_Sweep |
| 4 | +// #2025_02_18_Time_156_ms_(80.18%)_Space_79.96_MB_(64.32%) |
4 | 5 |
|
| 6 | +import java.util.ArrayList; |
5 | 7 | import java.util.Arrays; |
6 | | -import java.util.Comparator; |
| 8 | +import java.util.List; |
7 | 9 |
|
8 | 10 | public class Solution { |
9 | | - public double separateSquares(int[][] squares) { |
10 | | - int n = squares.length; |
11 | | - int m = 2 * n; |
12 | | - Event[] events = createEvents(squares, m); |
13 | | - double[] xsRaw = createXsRaw(squares, m); |
14 | | - Arrays.sort(events, Comparator.comparingDouble(e -> e.y)); |
15 | | - double[] xs = compress(xsRaw); |
16 | | - double totalUnionArea = calculateTotalUnionArea(events, xs, m); |
17 | | - double target = totalUnionArea / 2.0; |
18 | | - return findSplitPoint(events, xs, m, target); |
19 | | - } |
20 | | - |
21 | | - private Event[] createEvents(int[][] squares, int m) { |
22 | | - Event[] events = new Event[m]; |
23 | | - int idx = 0; |
24 | | - for (int[] sq : squares) { |
25 | | - double x = sq[0]; |
26 | | - double y = sq[1]; |
27 | | - double l = sq[2]; |
28 | | - double x2 = x + l; |
29 | | - double y2 = y + l; |
30 | | - events[idx++] = new Event(y, x, x2, 1); |
31 | | - events[idx++] = new Event(y2, x, x2, -1); |
32 | | - } |
33 | | - return events; |
34 | | - } |
35 | | - |
36 | | - private double[] createXsRaw(int[][] squares, int m) { |
37 | | - double[] xsRaw = new double[m]; |
38 | | - int xIdx = 0; |
39 | | - for (int[] sq : squares) { |
40 | | - double x = sq[0]; |
41 | | - double l = sq[2]; |
42 | | - xsRaw[xIdx++] = x; |
43 | | - xsRaw[xIdx++] = x + l; |
44 | | - } |
45 | | - return xsRaw; |
46 | | - } |
47 | | - |
48 | | - private double calculateTotalUnionArea(Event[] events, double[] xs, int m) { |
49 | | - SegmentTree segTree = new SegmentTree(xs); |
50 | | - double totalUnionArea = 0.0; |
51 | | - double lastY = events[0].y; |
52 | | - int i = 0; |
53 | | - while (i < m) { |
54 | | - double curY = events[i].y; |
55 | | - if (curY > lastY) { |
56 | | - double unionX = segTree.query(); |
57 | | - totalUnionArea += unionX * (curY - lastY); |
58 | | - lastY = curY; |
59 | | - } |
60 | | - while (i < m && events[i].y == curY) { |
61 | | - int[] indices = findIndices(xs, events[i]); |
62 | | - segTree.update(1, 0, xs.length - 1, indices[0], indices[1], events[i].type); |
63 | | - i++; |
64 | | - } |
65 | | - } |
66 | | - return totalUnionArea; |
67 | | - } |
68 | | - |
69 | | - private double findSplitPoint(Event[] events, double[] xs, int m, double target) { |
70 | | - SegmentTree segTree = new SegmentTree(xs); |
71 | | - double lastY = events[0].y; |
72 | | - double cumArea = 0.0; |
73 | | - int i = 0; |
74 | | - while (i < m) { |
75 | | - double curY = events[i].y; |
76 | | - if (curY > lastY) { |
77 | | - double unionX = segTree.query(); |
78 | | - double dy = curY - lastY; |
79 | | - if (cumArea + unionX * dy >= target - 1e-10) { |
80 | | - return lastY + (target - cumArea) / unionX; |
81 | | - } |
82 | | - cumArea += unionX * dy; |
83 | | - lastY = curY; |
84 | | - } |
85 | | - while (i < m && events[i].y == curY) { |
86 | | - int[] indices = findIndices(xs, events[i]); |
87 | | - segTree.update(1, 0, xs.length - 1, indices[0], indices[1], events[i].type); |
88 | | - i++; |
89 | | - } |
90 | | - } |
91 | | - return lastY; |
92 | | - } |
93 | | - |
94 | | - private int[] findIndices(double[] xs, Event event) { |
95 | | - int lIdx = Arrays.binarySearch(xs, event.x1); |
96 | | - if (lIdx < 0) { |
97 | | - lIdx = -lIdx - 1; |
98 | | - } |
99 | | - int rIdx = Arrays.binarySearch(xs, event.x2); |
100 | | - if (rIdx < 0) { |
101 | | - rIdx = -rIdx - 1; |
102 | | - } |
103 | | - return new int[] {lIdx, rIdx}; |
104 | | - } |
105 | | - |
106 | | - private double[] compress(double[] arr) { |
107 | | - Arrays.sort(arr); |
108 | | - int cnt = 1; |
109 | | - for (int i = 1; i < arr.length; i++) { |
110 | | - if (arr[i] != arr[i - 1]) { |
111 | | - cnt++; |
112 | | - } |
113 | | - } |
114 | | - double[] res = new double[cnt]; |
115 | | - res[0] = arr[0]; |
116 | | - int j = 1; |
117 | | - for (int i = 1; i < arr.length; i++) { |
118 | | - if (arr[i] != arr[i - 1]) { |
119 | | - res[j++] = arr[i]; |
120 | | - } |
121 | | - } |
122 | | - return res; |
123 | | - } |
124 | | - |
125 | | - private static class Event { |
| 11 | + private static class Event implements Comparable<Event> { |
126 | 12 | double y; |
127 | | - double x1; |
128 | | - double x2; |
| 13 | + int x1; |
| 14 | + int x2; |
129 | 15 | int type; |
130 | 16 |
|
131 | | - Event(double y, double x1, double x2, int type) { |
| 17 | + Event(double y, int x1, int x2, int type) { |
132 | 18 | this.y = y; |
133 | 19 | this.x1 = x1; |
134 | 20 | this.x2 = x2; |
135 | 21 | this.type = type; |
136 | 22 | } |
| 23 | + |
| 24 | + public int compareTo(Event other) { |
| 25 | + return Double.compare(this.y, other.y); |
| 26 | + } |
| 27 | + } |
| 28 | + |
| 29 | + private static class Segment { |
| 30 | + double y1; |
| 31 | + double y2; |
| 32 | + double unionX; |
| 33 | + double cumArea; |
| 34 | + |
| 35 | + Segment(double y1, double y2, double unionX, double cumArea) { |
| 36 | + this.y1 = y1; |
| 37 | + this.y2 = y2; |
| 38 | + this.unionX = unionX; |
| 39 | + this.cumArea = cumArea; |
| 40 | + } |
137 | 41 | } |
138 | 42 |
|
139 | 43 | private static class SegmentTree { |
140 | | - int n; |
141 | | - double[] tree; |
142 | 44 | int[] count; |
143 | | - double[] xs; |
| 45 | + double[] len; |
| 46 | + int n; |
| 47 | + int[] x; |
144 | 48 |
|
145 | | - SegmentTree(double[] xs) { |
146 | | - this.xs = xs; |
147 | | - this.n = xs.length; |
148 | | - tree = new double[4 * n]; |
| 49 | + SegmentTree(int[] x) { |
| 50 | + this.x = x; |
| 51 | + n = x.length - 1; |
149 | 52 | count = new int[4 * n]; |
| 53 | + len = new double[4 * n]; |
150 | 54 | } |
151 | 55 |
|
152 | 56 | void update(int idx, int l, int r, int ql, int qr, int val) { |
153 | | - if (qr <= l || ql >= r) { |
| 57 | + if (qr < l || ql > r) { |
154 | 58 | return; |
155 | 59 | } |
156 | 60 | if (ql <= l && r <= qr) { |
157 | 61 | count[idx] += val; |
158 | 62 | } else { |
159 | | - int mid = (l + r) >> 1; |
160 | | - update(idx << 1, l, mid, ql, qr, val); |
161 | | - update(idx << 1 | 1, mid, r, ql, qr, val); |
| 63 | + int mid = (l + r) / 2; |
| 64 | + update(2 * idx + 1, l, mid, ql, qr, val); |
| 65 | + update(2 * idx + 2, mid + 1, r, ql, qr, val); |
162 | 66 | } |
163 | 67 | if (count[idx] > 0) { |
164 | | - tree[idx] = xs[r] - xs[l]; |
165 | | - } else if (r - l == 1) { |
166 | | - tree[idx] = 0; |
| 68 | + len[idx] = x[r + 1] - x[l]; |
167 | 69 | } else { |
168 | | - tree[idx] = tree[idx << 1] + tree[idx << 1 | 1]; |
| 70 | + if (l == r) { |
| 71 | + len[idx] = 0; |
| 72 | + } else { |
| 73 | + len[idx] = len[2 * idx + 1] + len[2 * idx + 2]; |
| 74 | + } |
169 | 75 | } |
170 | 76 | } |
171 | 77 |
|
| 78 | + void update(int ql, int qr, int val) { |
| 79 | + update(0, 0, n - 1, ql, qr, val); |
| 80 | + } |
| 81 | + |
172 | 82 | double query() { |
173 | | - return tree[1]; |
| 83 | + return len[0]; |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + public double separateSquares(int[][] squares) { |
| 88 | + int n = squares.length; |
| 89 | + Event[] events = new Event[2 * n]; |
| 90 | + int idx = 0; |
| 91 | + List<Integer> xList = new ArrayList<>(); |
| 92 | + for (int[] s : squares) { |
| 93 | + int x = s[0]; |
| 94 | + int y = s[1]; |
| 95 | + int l = s[2]; |
| 96 | + int x2 = x + l; |
| 97 | + int y2 = y + l; |
| 98 | + events[idx++] = new Event(y, x, x2, 1); |
| 99 | + events[idx++] = new Event(y2, x, x2, -1); |
| 100 | + xList.add(x); |
| 101 | + xList.add(x2); |
| 102 | + } |
| 103 | + Arrays.sort(events); |
| 104 | + int m = xList.size(); |
| 105 | + int[] xCords = new int[m]; |
| 106 | + for (int i = 0; i < m; i++) { |
| 107 | + xCords[i] = xList.get(i); |
| 108 | + } |
| 109 | + Arrays.sort(xCords); |
| 110 | + int uniqueCount = 0; |
| 111 | + for (int i = 0; i < m; i++) { |
| 112 | + if (i == 0 || xCords[i] != xCords[i - 1]) { |
| 113 | + xCords[uniqueCount++] = xCords[i]; |
| 114 | + } |
| 115 | + } |
| 116 | + int[] x = Arrays.copyOf(xCords, uniqueCount); |
| 117 | + SegmentTree segTree = new SegmentTree(x); |
| 118 | + List<Segment> segments = new ArrayList<>(); |
| 119 | + double cumArea = 0.0; |
| 120 | + double lastY = events[0].y; |
| 121 | + int iEvent = 0; |
| 122 | + while (iEvent < events.length) { |
| 123 | + double currentY = events[iEvent].y; |
| 124 | + double delta = currentY - lastY; |
| 125 | + if (delta > 0) { |
| 126 | + double unionX = segTree.query(); |
| 127 | + segments.add(new Segment(lastY, currentY, unionX, cumArea)); |
| 128 | + cumArea += unionX * delta; |
| 129 | + } |
| 130 | + while (iEvent < events.length && events[iEvent].y == currentY) { |
| 131 | + Event e = events[iEvent]; |
| 132 | + int left = Arrays.binarySearch(x, e.x1); |
| 133 | + int right = Arrays.binarySearch(x, e.x2); |
| 134 | + if (left < right) { |
| 135 | + segTree.update(left, right - 1, e.type); |
| 136 | + } |
| 137 | + iEvent++; |
| 138 | + } |
| 139 | + lastY = currentY; |
| 140 | + } |
| 141 | + double totalArea = cumArea; |
| 142 | + double target = totalArea / 2.0; |
| 143 | + double answer; |
| 144 | + for (Segment seg : segments) { |
| 145 | + double segArea = seg.unionX * (seg.y2 - seg.y1); |
| 146 | + if (seg.cumArea + segArea >= target) { |
| 147 | + double needed = target - seg.cumArea; |
| 148 | + answer = seg.y1 + needed / seg.unionX; |
| 149 | + return answer; |
| 150 | + } |
174 | 151 | } |
| 152 | + return lastY; |
175 | 153 | } |
176 | 154 | } |
0 commit comments