题目大意

在平面上给定n个矩形,可以相互覆盖全部或者部分,求出矩形占据的总面积。

题目分析

将矩形按照x方向的进行分割之后,将平面沿着y方向划分一系列单元(不定高度),每个矩形在y方向上占据若干连续的单元;在x方向上,将矩形按照x坐标排序之后,考虑有一个扫描线从左到右扫描,当扫描线进入矩形之后,所有矩形在扫描线上占据的总长度有可能增加,而扫面线离开某个矩形时,所有矩形在扫描线上占据的总长度有可能减少。 
    在计算面积的时候,将当前扫描点 所有矩形在扫描线上占据的总长度 乘以 当前扫描点到下一扫描点的长度,直到所有矩形均出扫描线。区间操作,考虑使用线段树

实现(c++)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
#define MAX_RECT_NUM 1000
#define MAX_SEG_NUM MAX_RECT_NUM * 2
#define MAX_NODE_NUM 4*MAX_SEG_NUM
#define MAX(a, b) a > b? a :b
#define MIN(a, b) a < b? a :b
//根据矩形的上下边在y方向上划分区间单元(长度不固定),每个矩形占据y方向上的连续的几个单元,形成区间
struct Rect{
double top_left_x;
double top_left_y;
double bottom_right_x;
double bottom_right_y; int interval_beg; //在y轴上,该矩形所占据区间的起始单元序号
int interval_end; //在y轴上,该矩形所占据区间的结束单元序号(从下向上) inteval_beg 和 interval_end为 闭区间
}; Rect gRects[MAX_RECT_NUM];
vector<double> gPartPoint; //用于离散化的点纵坐标
vector<double> gSegs; //离散化之后的段单元长度 struct Node{
int beg; //在y轴方向离散化之后,节点所代表区间的起始块号
int end; //节点所代表区间的终止块号
double length; //节点所代表区间的长度(y轴方向)
int covered_num; //扫描线被多少个矩形覆盖
};
Node gNodes[MAX_NODE_NUM]; //对矩形进行x坐标从小到大排序,便于进行扫描
bool CmpToSortRect(const Rect& rect1, const Rect& rect2){
if (rect1.top_left_x == rect2.top_left_x)
return rect1.bottom_right_x < rect2.bottom_right_x;
return rect1.top_left_x < rect2.top_left_x;
} void BuildTree(int beg, int end, int index){
gNodes[index].beg = beg;
gNodes[index].end = end;
gNodes[index].covered_num = 0;
if (beg == end){
gNodes[index].length = gSegs[beg];
return;
}
int left = 2 * index + 1;
int right = 2 * index + 2;
int mid = (beg + end) / 2;
BuildTree(beg, mid, left);
BuildTree(mid + 1, end, right);
//由子节点长度得到父节点代表区间的长度
gNodes[index].length = gNodes[left].length + gNodes[right].length;
} //向下更新
void PushDown(int index){
if (gNodes[index].covered_num){
int left = 2 * index + 1, right = 2 * index + 2;
gNodes[left].covered_num += gNodes[index].covered_num;
gNodes[right].covered_num += gNodes[index].covered_num;
}
gNodes[index].covered_num = 0;
} //向上更新
void PushUp(int index){
int left = 2 * index + 1, right = 2 * index + 2;
int min = MIN(gNodes[left].covered_num, gNodes[right].covered_num);
gNodes[index].covered_num = min;
gNodes[left].covered_num -= min;
gNodes[right].covered_num -= min;
} //当扫描线进入矩形区域时step_in = true, 否则为false
void Update(int beg, int end, int index, bool step_in){
if (gNodes[index].beg >= beg && gNodes[index].end <= end){
if (step_in){
gNodes[index].covered_num++;
} else{
gNodes[index].covered_num--;
}
return;
}
if (gNodes[index].end < beg || gNodes[index].beg > end){
return;
}
if (beg > end){
return;
}
int left = 2 * index + 1, right = 2 * index + 2;
int mid = (gNodes[index].beg + gNodes[index].end) / 2;
//向下递归时,先pushdown 向下更新
PushDown(index);
Update(beg, MIN(mid, end), left, step_in);
Update(MAX(mid + 1, beg), end, right, step_in);
//递归返回进行 向上更新
PushUp(index);
} //查询,查询当前情况下,扫描线占据的矩形y方向长度
double Query(int index){
if (gNodes[index].covered_num > 0){
return gNodes[index].length;
}
if (gNodes[index].beg == gNodes[index].end){
return 0;
}
int left = 2 * index + 1, right = 2 * index + 2;
return Query(left) + Query(right);
} bool DoubleEqual(double d1, double d2){
if (abs(d1 - d2) < 1e-7){
return true;
}
return false;
}
int main(){
int n, cas = 1;
while (true){
scanf("%d", &n);
if (n == 0){
break;
}
gPartPoint.clear();
for (int i = 0; i < n; i++){
scanf("%lf %lf %lf %lf", &gRects[i].top_left_x, &gRects[i].top_left_y, &gRects[i].bottom_right_x, &gRects[i].bottom_right_y);
gPartPoint.push_back(gRects[i].top_left_y); //得到y方向上的各个离散的分界点
gPartPoint.push_back(gRects[i].bottom_right_y);
}
//对分界点进行排序,去重
sort(gPartPoint.begin(), gPartPoint.end());
vector<double>::iterator it = unique(gPartPoint.begin(), gPartPoint.end());
gPartPoint.erase(it, gPartPoint.end()); //根据分界点,得到离散化之后的区间长度
gSegs.clear();
gSegs.reserve(gPartPoint.size());
for (int i = 0; i < gPartPoint.size() - 1; i++){
double len = gPartPoint[i + 1] - gPartPoint[i];
gSegs.push_back(len);
} //得到每个矩形在y方向上占据的离散化之后的区间的 beg和end(闭区间)
for (int i = 0; i < n; i++){
vector<double>::iterator it = find(gPartPoint.begin(), gPartPoint.end(), gRects[i].top_left_y);
gRects[i].interval_beg = it - gPartPoint.begin();
it = find(gPartPoint.begin(), gPartPoint.end(), gRects[i].bottom_right_y);
gRects[i].interval_end = it - gPartPoint.begin() - 1;
} BuildTree(0, gSegs.size() - 1, 0); //将x方向的各个分割点进行排序,去重
gPartPoint.clear();
for (int i = 0; i < n; i++){
gPartPoint.push_back(gRects[i].top_left_x);
gPartPoint.push_back(gRects[i].bottom_right_x);
}
sort(gPartPoint.begin(), gPartPoint.end());
it = unique(gPartPoint.begin(), gPartPoint.end());
gPartPoint.erase(it, gPartPoint.end()); int seg_num = gSegs.size();
double sum_area = 0;
double height = 0;
int beg, end;
for (int i = 0; i < gPartPoint.size() - 1; i++){ for (int r = 0; r < n; r++){
if (DoubleEqual(gRects[r].top_left_x, gPartPoint[i])){ //扫描线进入矩形
Update(gRects[r].interval_beg, gRects[r].interval_end, 0, true);
}
if (DoubleEqual(gRects[r].bottom_right_x, gPartPoint[i])){//扫描线离开矩形
Update(gRects[r].interval_beg, gRects[r].interval_end, 0, false);
} }
height = Query(0);
sum_area += height*(gPartPoint[i + 1] - gPartPoint[i]);
}
printf("Test case #%d\n", cas ++);
printf("Total explored area: %.2lf\n\n", sum_area);
}
return 0;
}

poj_1151 线段树的更多相关文章

  1. bzoj3932--可持久化线段树

    题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...

  2. codevs 1082 线段树练习 3(区间维护)

    codevs 1082 线段树练习 3  时间限制: 3 s  空间限制: 128000 KB  题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区 ...

  3. codevs 1576 最长上升子序列的线段树优化

    题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...

  4. codevs 1080 线段树点修改

    先来介绍一下线段树. 线段树是一个把线段,或者说一个区间储存在二叉树中.如图所示的就是一棵线段树,它维护一个区间的和. 蓝色数字的是线段树的节点在数组中的位置,它表示的区间已经在图上标出,它的值就是这 ...

  5. codevs 1082 线段树区间求和

    codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...

  6. PYOJ 44. 【HNSDFZ2016 #6】可持久化线段树

    #44. [HNSDFZ2016 #6]可持久化线段树 统计 描述 提交 自定义测试 题目描述 现有一序列 AA.您需要写一棵可持久化线段树,以实现如下操作: A v p x:对于版本v的序列,给 A ...

  7. CF719E(线段树+矩阵快速幂)

    题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...

  8. 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

    3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] ...

  9. 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status ...

随机推荐

  1. 浅谈WebService的调用<转>

    0.前言 前段时间,公司和电信有个合作,产品对接电信的某个平台,使用了WebService接口的调用,实现了业务受理以及单点登录.终于使用到了WebService,楼主还是比较兴奋的,目前功能已经上线 ...

  2. 【C】——指针与const限定符

    const限定符和指针结合起来常见的情况有以下几种. const int *a; int const *a; 这两种写法是一样的,a是一个指向const int型的指针,a所指向的内存单元不可改写,所 ...

  3. jQuery实现布局高宽自适应

    在页面布局(layout)时经常是上左右的框架布局并且需要宽.高度的自适应,div+css是无法实现(*hegz:div+css其实是可以实现的,利用jQuery比较容易实现浏览器的兼容性),所以需要 ...

  4. JQuery下拉控件select的操作汇总

    JQuery获取和设置Select选项方法汇总如下: 获取select 先看看下面代码:   $("#select_id").change(function(){//code... ...

  5. 【这特么是个坑。。。】iOS 10.3下解决Charles抓包ssl证书信任问题

    针对近期iOS 10.3以上的系统charles抓https信任问题 前言 最近iPhone系统更新到ios 10.3后,在公司里用Charles抓包竟然出现了一些问题,https的请求都会失败,提示 ...

  6. 关于Cocos2d-x的专属数据类型

    1.Size类定义的实例是一个有width和height属性的类 Size s = Size(44,52); 其中 s.width=44 s.height=52 2.Vec2是一个带有两个变量的(常量 ...

  7. Maven学习日记(二)---MAVEN创建多模块的项目

    手动构建多模块maven项目,这个simple-parent项目下有两个子模块,一个是jar包型的simple-weather和一个war型的simple-webapp1.创建一个父的simple-p ...

  8. 【转】MFC CListCtrl 使用技巧

    以下未经说明,listctrl默认view 风格为report 相关类及处理函数 MFC:CListCtrl类 SDK:以 “ListView_”开头的一些宏.如 ListView_InsertCol ...

  9. QHeaderView的点击和双击事件

    QTablewidget的horizontalHeader() 和 verticalHeader() 得到的表头:QHeaderView 点击事件的触发函数: sectionClicked(int) ...

  10. Windows下安装Scrapy

    安装python 根据你的需求下载python安装包,安装python(本文基于python27)https://www.python.org/downloads/ 在 环境变量---"Pa ...