poj_1151 线段树
题目大意
在平面上给定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 线段树的更多相关文章
- bzoj3932--可持久化线段树
题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...
- codevs 1082 线段树练习 3(区间维护)
codevs 1082 线段树练习 3 时间限制: 3 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区 ...
- codevs 1576 最长上升子序列的线段树优化
题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...
- codevs 1080 线段树点修改
先来介绍一下线段树. 线段树是一个把线段,或者说一个区间储存在二叉树中.如图所示的就是一棵线段树,它维护一个区间的和. 蓝色数字的是线段树的节点在数组中的位置,它表示的区间已经在图上标出,它的值就是这 ...
- codevs 1082 线段树区间求和
codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...
- PYOJ 44. 【HNSDFZ2016 #6】可持久化线段树
#44. [HNSDFZ2016 #6]可持久化线段树 统计 描述 提交 自定义测试 题目描述 现有一序列 AA.您需要写一棵可持久化线段树,以实现如下操作: A v p x:对于版本v的序列,给 A ...
- CF719E(线段树+矩阵快速幂)
题意:给你一个数列a,a[i]表示斐波那契数列的下标为a[i],求区间对应斐波那契数列数字的和,还要求能够维护对区间内所有下标加d的操作 分析:线段树 线段树的每个节点表示(f[i],f[i-1])这 ...
- 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序
3779: 重组病毒 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 224 Solved: 95[Submit][Status][Discuss] ...
- 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集
3673: 可持久化并查集 by zky Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 1878 Solved: 846[Submit][Status ...
随机推荐
- C语言 · 淘淘的名单
算法提高 淘淘的名单 时间限制:100ms 内存限制:8.0MB 问题描述 by ZBY... :) 淘淘拿到了一份名单,他想对上面的名字进行处理,挑出一些特殊的名字,他请你来帮忙. ...
- GB2312汉字区位码、交换码和机内码转换方法 (ZT)
GB2312汉字区位码.交换码和机内码转换方法 (ZT) 为了适应计算机处理汉字信息的需要,1981年我国颁布了GB2312国家标准.该标准选出6763个常用汉字(其中,一级常用汉字3755个,二级汉 ...
- Storm快速理解
转自:http://blog.csdn.net/colorant/article/details/8256039 更多云计算相关项目快速理解文档 http://blog.csdn.net/color ...
- Node.js进程通信模块child_process
前言 Node.js是一种单线程的编程模型,对Node.js的赞美和诟病的也都是因为它的单线程模型,所有的任务都在一个线程中完成(I/O等例外).单线程模型,不仅让代码非常简洁,更是直接避免了线程调度 ...
- CentOS下rpm指令和yum指令详解
centos的软件安装大致可以分为两种类型: [centos]rpm文件安装,使用rpm指令 类似[ubuntu]deb文件安装,使用dpkg指令 [centos]yum安装 类似[ubuntu]ap ...
- 【Java 线程的深入研究2】常用函数说明
①sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) ②join():指等待t线程终止. 使用方式. join是Thread类的一个方法,启动线程后直接调用, ...
- informatica中的workflow连接远程数据库
如果是远程oracle这样写 名称随便起,方便自己记住,后面用户名密码你都知道,再加上数据库的地址:端口/SID就可以了. 如10.33.2.208:1521/torcl
- SharePoint Server 2013 通过IP无法访问站点
通过IP访问SharePoint站点,出现“The Web application at http://172.21.19.132:1000 could not be found.... ”如下错误: ...
- delphi程序热键
要定义一个全局热键,通常有三个步骤: 1.定义Windows的消息WM_HOTKEY的HOOK链,即 procedure MyShortCut(Var Message: ...
- ini 文件操作记要(2): 使用 TMemIniFile
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Form ...