算法:线段树+离散化+扫描线
思想:把矩形的两条侧边存起来,然后排序,所有的y坐标存起来,也排序。映射到一个整数值。
每次遇到一个边先求面积,如果是左边的话插入其y方向的测度,如果是右侧边的话删除y方向的测度。不断的向右扫描。
线段树优化保存y方向的测度。使询问由o(n)变为o(logn)
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; #define LEFT(i) (i * 2) #define RIGHT(i) (i * 2 + 1) const int SIZE_OF_ARR = 256; struct Edge { double x, y1, y2; bool flag; bool operator<(const Edge a) const { return x < a.x; } } edge[SIZE_OF_ARR]; double arry[SIZE_OF_ARR]; class segment_tree { private: const static int SIZE = 1001 * 8; int left[SIZE], right[SIZE], cnt[SIZE]; double m[SIZE]; void build(int l, int r, int n); public: void initialize(int n); void insert(int l, int r, int n); void del(int l, int r, int n); double queury(); void update(int n); }; void segment_tree::del(int l, int r, int n) { int mid = (right[n] + left[n]) / 2; if (l == left[n] && r == right[n]) cnt[n]--; else { if (mid <= l) del(l, r, RIGHT(n)); else if (r <= mid) del(l, r, LEFT(n)); else { del(l, mid, LEFT(n)); del(mid, r, RIGHT(n)); } } update(n); } void segment_tree::update(int n) { if (cnt[n]) m[n] = arry[right[n]] - arry[left[n]]; else { if (right[n] - left[n] == 1) m[n] = 0; else m[n] = m[LEFT(n)] + m[RIGHT(n)]; } } void segment_tree::insert(int l, int r, int n) { int mid = (right[n] + left[n]) / 2; if (l == left[n] && r == right[n]) cnt[n]++; else { if (mid <= l) insert(l, r, RIGHT(n)); else if (r <= mid) insert(l, r, LEFT(n)); else { insert(l, mid, LEFT(n)); insert(mid, r, RIGHT(n)); } } update(n); } double segment_tree::queury() { return m[1]; } void segment_tree::initialize(int n) { memset(m, 0, sizeof (m)); memset(cnt, 0, sizeof (cnt)); memset(left, 0, sizeof (left)); memset(right, 0, sizeof (right)); build(0, n, 1); } void segment_tree::build(int l, int r, int n) { left[n] = l; right[n] = r; m[n] = 0; int mid = (l + r) / 2; if (r - l <= 1) return; build(l, mid, LEFT(n)); build(mid, r, RIGHT(n)); } segment_tree tree; int main() { int n, cs = 1; while (scanf("%d", &n) == 1 && n) { double x1, x2, y1, y2; for (int i = 0; i < n; i++) { scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2); edge[i * 2].x = x1; edge[i * 2 + 1].y1 = edge[i * 2].y1 = min(y1, y2); edge[i * 2 + 1].x = x2; edge[i * 2 + 1].y2 = edge[i * 2].y2 = max(y1,y2); arry[i * 2] = y1; arry[i * 2 + 1] = y2; if (x1 > x2) { edge[i * 2].flag = true; edge[i * 2 + 1].flag = false; } else { edge[i * 2].flag = false; edge[i * 2 + 1].flag = true; } } sort(edge, edge + n * 2); sort(arry, arry + n * 2); tree.initialize(n * 2); double area = 0; for (int i = 0; i < 2 * n; i++) { int a = lower_bound(arry, arry + 2 * n, edge[i].y1) - arry; int b = lower_bound(arry, arry + 2 * n, edge[i].y2) - arry; if (a == b) continue; if (i == 0) { tree.insert(a, b, 1); continue; } area += (edge[i].x - edge[i - 1].x) * tree.queury(); if (edge[i].flag) tree.del(a, b, 1); else tree.insert(a, b, 1); } printf("Test case #%d\nTotal explored area: %0.2lf\n\n", cs++, area); } return 0; }