120 lines
3.2 KiB
Java
120 lines
3.2 KiB
Java
import java.awt.Rectangle;
|
|
import java.awt.geom.*;
|
|
import java.util.*;
|
|
|
|
public class RectangleUnion {
|
|
|
|
static class CoverageTree {
|
|
int[] count;
|
|
int[] len;
|
|
int[] y;
|
|
|
|
public CoverageTree(int[] y) {
|
|
count = new int[4 * y.length];
|
|
len = new int[4 * y.length];
|
|
this.y = y;
|
|
}
|
|
|
|
public void update(int from, int to, int delta) {
|
|
update(from, to, delta, 0, 0, y.length - 1);
|
|
}
|
|
|
|
void update(int from, int to, int delta, int root, int left, int right) {
|
|
if (from == left && to == right) {
|
|
count[root] += delta;
|
|
} else {
|
|
int mid = (left + right) >> 1;
|
|
if (from <= mid)
|
|
update(from, Math.min(to, mid), delta, 2 * root + 1, left, mid);
|
|
if (to > mid)
|
|
update(Math.max(from, mid + 1), to, delta, 2 * root + 2, mid + 1, right);
|
|
}
|
|
len[root] = count[root] != 0 ? y[right + 1] - y[left] : right > left ? len[2 * root + 1] + len[2 * root + 2] : 0;
|
|
}
|
|
}
|
|
|
|
public static long unionArea(Rectangle[] rectangles) {
|
|
int n = rectangles.length;
|
|
long[] events = new long[2 * n];
|
|
int[] y = new int[2 * n];
|
|
for (int i = 0; i < n; i++) {
|
|
Rectangle rect = rectangles[i];
|
|
int x1 = rect.x;
|
|
int x2 = rect.x + rect.width;
|
|
int y1 = rect.y;
|
|
int y2 = rect.y + rect.height;
|
|
events[2 * i] = ((long) x1 << 32) + i;
|
|
events[2 * i + 1] = ((long) x2 << 32) + (~i & 0xFFFF_FFFFL);
|
|
y[2 * i] = y1;
|
|
y[2 * i + 1] = y2;
|
|
}
|
|
Arrays.sort(events);
|
|
Arrays.sort(y);
|
|
CoverageTree t = new CoverageTree(y);
|
|
long area = 0;
|
|
int lastX = (int) (events[0] >>> 32);
|
|
for (long event : events) {
|
|
int i = (int) (event & 0xFFFF_FFFFL);
|
|
boolean in = i >= 0;
|
|
if (!in)
|
|
i = ~i;
|
|
int x = (int) (event >>> 32);
|
|
int dx = x - lastX;
|
|
int dy = t.len[0];
|
|
area += (long) dx * dy;
|
|
lastX = x;
|
|
|
|
int y1 = rectangles[i].y;
|
|
int y2 = rectangles[i].y + rectangles[i].height;
|
|
int from = Arrays.binarySearch(y, y1);
|
|
int to = Arrays.binarySearch(y, y2) - 1;
|
|
|
|
t.update(from, to, in ? 1 : -1);
|
|
}
|
|
|
|
return area;
|
|
}
|
|
|
|
// random test
|
|
public static void main(String[] args) {
|
|
Random rnd = new Random(1);
|
|
for (int step = 0; step < 1000; step++) {
|
|
int n = rnd.nextInt(100) + 1;
|
|
Rectangle[] rectangles = new Rectangle[n];
|
|
for (int i = 0; i < n; i++) {
|
|
int range = 100;
|
|
int x = rnd.nextInt(range) - range / 2;
|
|
int y = rnd.nextInt(range) - range / 2;
|
|
int width = rnd.nextInt(range) + 1;
|
|
int height = rnd.nextInt(range) + 1;
|
|
rectangles[i] = new Rectangle(x, y, width, height);
|
|
}
|
|
long res1 = unionArea(rectangles);
|
|
|
|
Area area = new Area();
|
|
for (Rectangle rectangle : rectangles)
|
|
area.add(new Area(rectangle));
|
|
List<Double> x = new ArrayList<>();
|
|
List<Double> y = new ArrayList<>();
|
|
double res2 = 0;
|
|
double[] coords = new double[6];
|
|
for (PathIterator pi = area.getPathIterator(null); !pi.isDone(); pi.next()) {
|
|
int type = pi.currentSegment(coords);
|
|
if (type == PathIterator.SEG_CLOSE) {
|
|
for (int i = 0, j = x.size() - 1; i < x.size(); j = i++)
|
|
res2 += x.get(i) * y.get(j) - x.get(j) * y.get(i);
|
|
x.clear();
|
|
y.clear();
|
|
} else {
|
|
x.add(coords[0]);
|
|
y.add(coords[1]);
|
|
}
|
|
}
|
|
res2 = Math.abs(res2) / 2;
|
|
|
|
if (!(Math.abs(res1 - res2) < 1e-9))
|
|
throw new RuntimeException();
|
|
}
|
|
}
|
|
}
|