HDU 1255 覆盖的面积
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1255
/**/
/*
题意:
给出N(N <= 1000)个矩形,求被覆盖2次以上的矩形面积并。
解法:
离散化+线段树
思路:
类似于覆盖一次的矩形面积并问题,还是用线段树求解,首先我们
将每个矩形的纵向边投影到Y轴上,这样就可以把矩形的纵向边看成一个
闭区间,用线段树来维护这些矩形边的并。现在求的是矩形的面积并,
于是可以枚举矩形的x坐标,然后检测当前相邻x坐标上y方向的合法长度,
两者相乘就是其中一块面积,枚举完毕后就求得了所有矩形的面积并。
我的线段树结点描述保存了以下信息:区间的左右端点、结点所在
数组编号(因为采用静态结点可以大大节省申请空间的时间)、该结点
被竖直线段完全覆盖的次数Cover和当前结点覆盖一次的y方向长度yOnce
和当前结点覆盖多次的y方向长度yMore。
其实和矩形面积并的唯一差别就是在计算Cover后的Update函数,更
新yOnce和yMore的值,分情况讨论:
1. 当nCover>1时,yOnce = 0; yMore = 区间实际长度;
2. 当nCover=1时,yMore = 两棵子树的yOnce+yMore;
yOnce = 区间实际长度 - yMore;
3. 当nCover=0时,如果是叶子结点 yOnce = 0; yMore = 0;
否则
yOnce = 两棵子树的yOnce和;
yMore = 两棵子树的yMore和;
*/
#include < iostream >
#include < algorithm >
#include < vector >
#include < cmath >
using namespace std;
#define maxn 2200
double tmp[maxn], bin[maxn];
int tmpsize, size;
struct Tree {
int p;
int l, r;
int nCover;
double ylenOnce, ylenMore;
void Update() {
if(nCover > 1) {
ylenOnce = 0;
ylenMore = bin[r] - bin[l];
}else if(nCover == 1) {
ylenMore = T[p<<1].ylenMore + T[p<<1].ylenOnce
+ T[p<<1|1].ylenMore + T[p<<1|1].ylenOnce;
ylenOnce = (bin[r] - bin[l]) - ylenMore;
}else {
if(l + 1 == r) {
ylenOnce = ylenMore = 0;
}else {
ylenOnce = T[p<<1].ylenOnce + T[p<<1|1].ylenOnce;
ylenMore = T[p<<1].ylenMore + T[p<<1|1].ylenMore;
}
}
}
} T[maxn * 4 ];
struct VLine {
double x;
double y1, y2;
int val;
VLine() {}
VLine(double _x, double _y1, double _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;
}
void Process() {
sort(tmp, tmp + tmpsize);
bin[ size = 1 ] = tmp[0];
for(int i = 1; i < tmpsize; i++) {
if(fabs(tmp[i] - tmp[i-1]) > 1e-6)
bin[ ++size ] = tmp[i];
}
}
int Binary( double v) {
int l = 1;
int r = size;
while(l <= r) {
int m = (l + r) >> 1;
if(fabs(v - bin[m]) < 1e-6)
return m;
if(v > bin[m])
l = m + 1;
else
r = m - 1;
}
}
void Build( int p, int l, int r) {
T[p].l = l; T[p].r = r;
T[p].p = p;
T[p].nCover = 0; T[p].ylenOnce = T[p].ylenMore = 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(r <= T[p].l || l >= T[p].r)
return ;
if(l <= T[p].l && T[p].r <= r) {
T[p].nCover += val;
T[p].Update();
return ;
}
Insert(p<<1, l, r, val);
Insert(p<<1|1, l, r, val);
T[p].Update();
}
int n;
int main() {
int t;
int i;
scanf("%d", &t);
while(t--) {
tmpsize = 0;
Vl.clear();
scanf("%d", &n);
for(i = 0; i < n; i++) {
double x0, x1, y0, y1;
scanf("%lf %lf %lf %lf", &x0, &y0, &x1, &y1);
Vl.push_back(VLine(x0, y0, y1, 1));
Vl.push_back(VLine(x1, y0, y1, -1));
tmp[ tmpsize++ ] = y0;
tmp[ tmpsize++ ] = y1;
}
sort(Vl.begin(), Vl.end(), cmp);
Process();
Build(1, 1, size);
double ans = 0;
for(i = 0; i < Vl.size(); i++) {
if(i) {
ans += (Vl[i].x - Vl[i-1].x) * T[1].ylenMore;
}
int y1 = Binary(Vl[i].y1);
int y2 = Binary(Vl[i].y2);
if(y1 < y2)
Insert(1, y1, y2, Vl[i].val);
}
printf("%.2lf\n", ans);
}
return 0;
}
题意:
给出N(N <= 1000)个矩形,求被覆盖2次以上的矩形面积并。
解法:
离散化+线段树
思路:
类似于覆盖一次的矩形面积并问题,还是用线段树求解,首先我们
将每个矩形的纵向边投影到Y轴上,这样就可以把矩形的纵向边看成一个
闭区间,用线段树来维护这些矩形边的并。现在求的是矩形的面积并,
于是可以枚举矩形的x坐标,然后检测当前相邻x坐标上y方向的合法长度,
两者相乘就是其中一块面积,枚举完毕后就求得了所有矩形的面积并。
我的线段树结点描述保存了以下信息:区间的左右端点、结点所在
数组编号(因为采用静态结点可以大大节省申请空间的时间)、该结点
被竖直线段完全覆盖的次数Cover和当前结点覆盖一次的y方向长度yOnce
和当前结点覆盖多次的y方向长度yMore。
其实和矩形面积并的唯一差别就是在计算Cover后的Update函数,更
新yOnce和yMore的值,分情况讨论:
1. 当nCover>1时,yOnce = 0; yMore = 区间实际长度;
2. 当nCover=1时,yMore = 两棵子树的yOnce+yMore;
yOnce = 区间实际长度 - yMore;
3. 当nCover=0时,如果是叶子结点 yOnce = 0; yMore = 0;
否则
yOnce = 两棵子树的yOnce和;
yMore = 两棵子树的yMore和;
*/
#include < iostream >
#include < algorithm >
#include < vector >
#include < cmath >
using namespace std;
#define maxn 2200
double tmp[maxn], bin[maxn];
int tmpsize, size;
struct Tree {
int p;
int l, r;
int nCover;
double ylenOnce, ylenMore;
void Update() {
if(nCover > 1) {
ylenOnce = 0;
ylenMore = bin[r] - bin[l];
}else if(nCover == 1) {
ylenMore = T[p<<1].ylenMore + T[p<<1].ylenOnce
+ T[p<<1|1].ylenMore + T[p<<1|1].ylenOnce;
ylenOnce = (bin[r] - bin[l]) - ylenMore;
}else {
if(l + 1 == r) {
ylenOnce = ylenMore = 0;
}else {
ylenOnce = T[p<<1].ylenOnce + T[p<<1|1].ylenOnce;
ylenMore = T[p<<1].ylenMore + T[p<<1|1].ylenMore;
}
}
}
} T[maxn * 4 ];
struct VLine {
double x;
double y1, y2;
int val;
VLine() {}
VLine(double _x, double _y1, double _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;
}
void Process() {
sort(tmp, tmp + tmpsize);
bin[ size = 1 ] = tmp[0];
for(int i = 1; i < tmpsize; i++) {
if(fabs(tmp[i] - tmp[i-1]) > 1e-6)
bin[ ++size ] = tmp[i];
}
}
int Binary( double v) {
int l = 1;
int r = size;
while(l <= r) {
int m = (l + r) >> 1;
if(fabs(v - bin[m]) < 1e-6)
return m;
if(v > bin[m])
l = m + 1;
else
r = m - 1;
}
}
void Build( int p, int l, int r) {
T[p].l = l; T[p].r = r;
T[p].p = p;
T[p].nCover = 0; T[p].ylenOnce = T[p].ylenMore = 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(r <= T[p].l || l >= T[p].r)
return ;
if(l <= T[p].l && T[p].r <= r) {
T[p].nCover += val;
T[p].Update();
return ;
}
Insert(p<<1, l, r, val);
Insert(p<<1|1, l, r, val);
T[p].Update();
}
int n;
int main() {
int t;
int i;
scanf("%d", &t);
while(t--) {
tmpsize = 0;
Vl.clear();
scanf("%d", &n);
for(i = 0; i < n; i++) {
double x0, x1, y0, y1;
scanf("%lf %lf %lf %lf", &x0, &y0, &x1, &y1);
Vl.push_back(VLine(x0, y0, y1, 1));
Vl.push_back(VLine(x1, y0, y1, -1));
tmp[ tmpsize++ ] = y0;
tmp[ tmpsize++ ] = y1;
}
sort(Vl.begin(), Vl.end(), cmp);
Process();
Build(1, 1, size);
double ans = 0;
for(i = 0; i < Vl.size(); i++) {
if(i) {
ans += (Vl[i].x - Vl[i-1].x) * T[1].ylenMore;
}
int y1 = Binary(Vl[i].y1);
int y2 = Binary(Vl[i].y2);
if(y1 < y2)
Insert(1, y1, y2, Vl[i].val);
}
printf("%.2lf\n", ans);
}
return 0;
}