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 ...
随机推荐
- plot sin 03-数据区域边界线的位置
plot sin 03 数据区域边界线的位置 Code #!/usr/bin/env python # -*- coding: utf-8 -*- import numpy as np import ...
- openwrt内核配置选项添加
摘自:http://blog.csdn.net/weiniliuchao/article/details/50295527 增加内核配置选项 openwrt的.config文件中,关于内核的选项都是形 ...
- find 命令一个命令多参数如何使用,????,perm
[root@ob2 mytmp]# find -mtime -7 -type f \( -name "*.html" -o -name "*.tar.gz" \ ...
- LINQ操作符三:限制操作符
where是限制操作符,它将过滤标准应用在序列上,按照提供的逻辑对序列中的数据进行过滤. where操作符不启动查询的执行.当开始对序列进行遍历时才开始执行,此时过滤条件将被应用到查询中. 示例: / ...
- jQuery的发展史,你知道吗?
2006年1月,jQuery的第一个版本面世,至今已经有6年多了(注:这个时间点是截止至出书时间).虽然过了这么久,但它依然以其简洁.灵活的编程风格让人一见倾心.在本篇文章中,我们将讲述jQuery的 ...
- HBase系统入门--整体介绍
转自:http://www.aboutyun.com/thread-8957-1-2.html 问题导读:1.HBase查询与写入哪个更好一些?2.HBase面对复杂操作能否实现?3.Region服务 ...
- 从my里调出数据插入到ms2008 骨架 php
<?php $server ="XEJMZWMDIXE9CIJ";//ms,服务器IP地址,如果是本地,可以写成localhost $uid =""; / ...
- 关于Unity中物理引擎的使用
物理引擎控制刚体,刚体上面贴上图片,所以看起来就是游戏运行起来的状态 物理世界计算刚体的运行状态,通过贴图显现出运算结果. 一.物理引擎 1:Unity 2D物理引擎基于Box2D封装而成;2: 物理 ...
- Android.mk文件c++头文件包含问题
Eclipse 中 Android.mk文件c++头文件包含问题 jni中的目录结构如下: 编译找不到头文件 LOCAL_PATH := $(call my-dir)LOCAL_C_INCLUDES ...
- linux -- Ubuntu Server 安装图形界面
1.连接网络,你一定要确保网络通畅,如果你和我一样使用Wireless,那先找根网线插上,因为下面的安装都要通过网络下载组件的. 2.进入图形界面的命令是startX,敲击后会有安装xinit的提示. ...