[矩形并-扫描线-线段树]Picture
最近在补数学和几何,没啥好写的,因为已经决定每天至少写一篇了,今天随便拿个题水水。
题目大意:给你N个边平行于坐标轴的矩形,求它们并的周长。(N<=5000)
思路:这个数据范围瞎暴力就过了,但我们是有文化的人,下面讲讲利用扫描线和线段树的简单O(NlogN)做法。
先讲扫描线。我们先只考虑横着的边,竖着的等会儿一样做就是了。我们假设有一条横着的直线(这条就是扫描线啦)从所有矩形的上方扫到所有矩形的下方,我们时刻维护这条线上各个位置分别被几个矩形覆盖,一开始肯定都是0,如果碰到一个矩形上方的边,我们先查看这条边上哪些部分还未被其他矩形覆盖,计入答案,然后把扫描线上的这一整条边的被覆盖次数加一;如果遇到一个矩形下方的边,同理我们先把扫描线上这一部分的被覆盖次数减一,看看那些部分已经未被其他矩形覆盖了(即被当前边最后覆盖),再计入答案,扫一遍所有矩形,就算完了。实现上我们可以把所有横的边拿出来,记下纵坐标和左右端点,以及是矩形上方还是下方的边,然后按纵坐标排个序就可以处理了。
那么如何维护呢?我们只要支持区间加减以及查询区间内0的个数,看上去线段树就能做,区间加减都是小Case,问题是如何查区间内0的个数?在区间加减的同时似乎不是很好维护。其实很简单,我们只要维护区间最小值和最小值个数就可以了,由我们维护的信息的意义可知,这些信息的最小值不会小于0,如果有0,查询最小值及个数时一定能被我们找到,维护最小值区间加减也很容易。网络上看见有人O(n)暴力维护的,还有线段树维护很多信息来计算的,感觉都不是很好,更有甚者在线段树上每次O(n)暴力维护,看了令人汗颜……
#include<cstdio>
#include<algorithm>
using namespace std;
#define MN 10000
#define MX 20000
#define L (k<<1)
#define R ((k<<1)+1)
struct work{int x,l,r,p;}x[MN+],y[MN+];
bool cmp(work a,work b){return a.x==b.x?a.p>b.p:a.x<b.x;}
struct data{int x,s;};
data operator+(data a,data b)
{
if(a.x==b.x)return(data){a.x,a.s+b.s};
return a.x<b.x?a:b;
}
struct node{int l,r,mk;data x;}t[MX*+];
inline void up(int k){t[k].x=t[L].x+t[R].x;}
inline void add(int k,int x){t[k].x.x+=x;t[k].mk+=x;}
inline void down(int k){if(t[k].mk)add(L,t[k].mk),add(R,t[k].mk),t[k].mk=;}
void build(int k,int l,int r)
{
t[k].l=l;t[k].r=r;
if(l==r){t[k].x.s=;return;}
int mid=l+r>>;
build(L,l,mid);build(R,mid+,r);
up(k);
}
void renew(int k,int l,int r,int x)
{
if(t[k].l==l&&t[k].r==r){add(k,x);return;}
down(k);
int mid=t[k].l+t[k].r>>;
if(r<=mid)renew(L,l,r,x);
else if(l>mid)renew(R,l,r,x);
else renew(L,l,mid,x),renew(R,mid+,r,x);
up(k);
}
data query(int k,int l,int r)
{
if(t[k].l==l&&t[k].r==r)return t[k].x;
down(k);
int mid=t[k].l+t[k].r>>;
if(r<=mid)return query(L,l,r);
if(l>mid)return query(R,l,r);
return query(L,l,mid)+query(R,mid+,r);
}
int n,ans;
void solve(work*x)
{
for(int i=;i<n;++i)
{
if(x[i].p<)renew(,x[i].l,x[i].r,-);
data d=query(,x[i].l,x[i].r);
if(x[i].p>)renew(,x[i].l,x[i].r,);
ans+=d.x?:d.s;
}
}
int main()
{
int i,x0,y0,x1,y1;
scanf("%d",&n);
for(i=;i<n;++i)
{
scanf("%d%d%d%d",&x0,&y0,&x1,&y1);
x0+=MN;y0+=MN;x1+=MN;y1+=MN;
x[i]=(work){y0,x0,x1-,};x[i+n]=(work){y1,x0,x1-,-};
y[i]=(work){x0,y0,y1-,};y[i+n]=(work){x1,y0,y1-,-};
}
n<<=;sort(x,x+n,cmp);sort(y,y+n,cmp);
build(,,MX);solve(x);solve(y);
printf("%d",ans);
}
[矩形并-扫描线-线段树]Picture的更多相关文章
- hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积
题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ...
- 【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树
[BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了 ...
- P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)
题面传送门 首先我们把这两个贡献翻译成人话: 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值. 区间 \([l ...
- HDU 3642 - Get The Treasury - [加强版扫描线+线段树]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3642 Time Limit: 10000/5000 MS (Java/Others) Memory L ...
- poj-1151矩形面积并-线段树
title: poj-1151矩形面积并-线段树 date: 2018-10-30 22:35:11 tags: acm 刷题 categoties: ACM-线段树 概述 线段树问题里的另一个问题, ...
- HDU 3265/POJ 3832 Posters(扫描线+线段树)(2009 Asia Ningbo Regional)
Description Ted has a new house with a huge window. In this big summer, Ted decides to decorate the ...
- 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树
题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...
- luogu P1856 [USACO5.5]矩形周长Picture 扫描线 + 线段树
Code: #include<bits/stdc++.h> #define maxn 200007 #define inf 100005 using namespace std; void ...
- 扫描线 + 线段树 : 求矩形面积的并 ---- hnu : 12884 Area Coverage
Area Coverage Time Limit: 10000ms, Special Time Limit:2500ms, Memory Limit:65536KB Total submit user ...
随机推荐
- mongodb 集群分片
分片 在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求 当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量,这 ...
- 详谈C++虚函数表那回事(多重继承关系)
上一篇说了一般继承,也就是单继承的虚函数表,接下来说说多重继承的虚函数表: 1.无虚函数覆盖的多重继承: 代码: #pragma once //无覆盖,多重继承 class Base1 { publi ...
- 利用java反射读写csv中的数据
前一段有个需求需要将从数据库读取到的信息保存到csv文件中,在实现该需求的时候发现资料比较少,经过收集反射和csv相关资料,最终得到了如下程序. 1.在使用java反射读取csv文件数据时,先通 ...
- 剑指offer-第一个只出现一次的字符
题目描述 在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置 解题思路 由于char类型一共有256种可能,所以开辟一个数组ha ...
- 增加Linux虚拟机的硬盘空间
原配置为40G,现需要增加到60G,操作方法如下: 一.虚拟机关机,在编辑设置里调整硬盘空间到60G 二.虚拟机开机,扩展硬盘空间 1.安装gparted,命令如下 sudo apt-get inst ...
- SpringBoot应用的启动方式
一:IDE 运行Application这个类的main方法 二:在SpringBoot的应用的根目录下运行mvn spring-boot:run 三:使用mvn install 生成jar后运行 先到 ...
- api-gateway实践(09)支持rest服务注册
一.GET-GET 1.前端定义 2.后端定义 2.1.基础定义 2.2.path参数.head参数.query参数 2.3.常量参数 2.4.系统参数 2.5.结果定义 二.POST-POST 1. ...
- NHibernate的基本使用
一.O/R Mapping 概论 工厂模式+反射+每个数据库的DAL层来解决数据访问层的代码 针对数据库表中字段的变化我们是无法预料的,所以每一次用户需求的修改都会直接导致我们程序员来修改—实体类(B ...
- [LuoguP1113] 杂物 - 拓扑排序
其实只是纪念下第一篇洛谷题解? Description John的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它.比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及 ...
- Java Servlet(十一):一个servlet被10个浏览器客户端访问时会创建几个servlet实例?
一般Servlet只初始化一次(只有一个实例).对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给该方法.如此重复以 ...