|
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