|
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3265
/**//* 题意: 给定N(N <= 50000)个中空的矩形纸片,求它们面积并。
解法: 离散化+线段树
思路: 2010年宁波区域赛的题,就是矩形面积并的一个变形,转化很容 易想到,将中空的矩形纸片分割成四个小的矩形然后求N*4个矩形的 面积并即可。 再总结一下矩形面积并的nlog(n)经典算法吧。首先我们将每个矩 形的纵向边投影到Y轴上,这样就可以把矩形的纵向边看成一个闭区间 ,用线段树来维护这些矩形边的并。现在求的是矩形的面积并,于是可 以枚举矩形的x坐标,然后检测当前相邻x坐标上y方向的合法长度,两 者相乘就是其中一块面积,枚举完毕后就求得了所有矩形的面积并。 我的线段树结点描述保存了以下信息:区间的左右端点、结点所在 数组编号(因为采用静态结点可以大大节省申请空间的时间)、该结点 被竖直线段完全覆盖的次数Cover和当前结点的测度。测度是指相邻x坐 标之间有效的y方向的长度的和(要求在该区间内)。于是重点就在于 如何维护测度,我们将一个矩形分成两条竖直线段来存储,左边的边称 为入边,右边的边则为出边,然后把所有这些竖直线段按照x坐标递增排 序,每次进行插入操作,因为坐标有可能不是整数,所以必须在做这些 之前将y方向的坐标离散化到数组中,每次插入时如果当前区间被完全覆 盖,那么就要对Cover域进行更新,入边+1,出边-1。更新完毕后判断当 前结点的Cover域是否大于零,如果大于零那么当前节点的测度就是结点 管理区间在y轴上的实际长度,否则,如果是叶子节点,那么测度为0, 如果是内部结点,那么测度的值是左右儿子测度的和。这个更新是log(n) 的,所以,总的复杂度就是nlog(n)。 */
#include <iostream> #include <vector> #include <algorithm> using namespace std;
typedef int Type; #define ll __int64 #define maxn 200200
// 垂直线段 struct VLine { Type x; Type y1, y2; int val; VLine() {} VLine(int _x, int _y1, int _y2, int _v) { x = _x; y1 = _y1; y2 = _y2; val = _v; } }; vector <VLine> Vl;
bool cmp(VLine a, VLine b) { return a.x < b.x; }
// 矩形 struct Rectangle { int x0, y0, x1, y1; Rectangle() {} Rectangle(int _x0, int _y0, int _x1, int _y1) { x0 = _x0; y0 = _y0; x1 = _x1; y1 = _y1; } }; vector <Rectangle> Rec;
int tmp[maxn], tmpsize; int bin[maxn], size;
struct Tree { int p; int l, r; int nCover; // 被完全覆盖的次数 Type ylen; // 测度
void Update() { if(nCover > 0) ylen = bin[r] - bin[l]; else { if(l + 1 == r) ylen = 0; else { ylen = T[p<<1].ylen + T[p<<1|1].ylen; } } }
int Mid() { return (l + r) >> 1; } }T[maxn*4];
void Build(int p, int l, int r) { T[p].l = l; T[p].r = r; T[p].p = p; T[p].ylen = T[p].nCover = 0; if(l + 1 == r || l == r) return ; int mid = (l + r) >> 1; Build(p<<1, l, mid); Build(p<<1|1, mid, r); }
void Insert(int p, int l, int r, int val) { if(l <= T[p].l && T[p].r <= r) { T[p].nCover += val; T[p].Update(); return ; }
int mid = T[p].Mid(); if(l < mid) { Insert(p<<1, l, r, val); } if(mid < r) { Insert(p<<1|1, l, r, val); } T[p].Update(); }
void ProcessBinArray() { sort(tmp, tmp + tmpsize); bin[size = 1] = tmp[0]; int i; for(i = 1; i < tmpsize; i++) { if(tmp[i] != tmp[i-1]) bin[++size] = tmp[i]; } }
int Binary(int v) { int l = 1; int r = size; while(l <= r) { int m = (l + r) >> 1; if(bin[m] == v) return m; if(v > bin[m]) { l = m + 1; }else r = m - 1; } }
int main() { int n; int i, j; Type x[4], y[4]; while(scanf("%d", &n) != EOF && n) { Rec.clear(); Vl.clear(); tmpsize = 0;
for(i = 0; i < n; i++) { for(j = 0; j < 4; j++) { scanf("%d %d", &x[j], &y[j]); tmp[ tmpsize++ ] = y[j]; } Rec.push_back(Rectangle(x[0], y[0], x[2], y[1])); Rec.push_back(Rectangle(x[2], y[0], x[3], y[2])); Rec.push_back(Rectangle(x[2], y[3], x[3], y[1])); Rec.push_back(Rectangle(x[3], y[0], x[1], y[1])); } ProcessBinArray();
for(i = 0; i < Rec.size(); i++) { Rectangle& rt = Rec[i]; if(rt.x0 == rt.x1 || rt.y0 == rt.y1) continue; int y0 = Binary(rt.y0); int y1 = Binary(rt.y1); Vl.push_back(VLine(rt.x0, y0, y1, 1)); Vl.push_back(VLine(rt.x1, y0, y1, -1)); } sort(Vl.begin(), Vl.end(), cmp); Build(1, 1, size);
ll ans = 0; for(i = 0; i < Vl.size(); i++) { if(i) { ans += (ll)(Vl[i].x - Vl[i-1].x) * T[1].ylen; } Insert(1, Vl[i].y1, Vl[i].y2, Vl[i].val); } printf("%I64d\n", ans); } return 0; }
|