POJ - 1177 线段树
POJ - 1177 扫描线
这道题也算是一道扫描线的经典题目了。
只不过这道题是算周长,非常有意思的一道题。我们已经知道了,一般求面积并,是如何求的,现在我们要把扫描线进行改造一下,使得能算周长。
我们大致考虑一下图像上是如何实现的:
这样一个图我们要如何求他的面积?

我们把轮廓画出来

我们把扫描线画出来

我们发现
从上到下我们竖直方向的长度,是每条线高度差*2*线段树的连续的段数目。
从上到下我们水平方向的长度,是横线的长度 = 现在这次总区间被覆盖的长度和上一次总区间被覆盖的长度之差的绝对值。
这样我们就找到解决的办法,维护就非常容易了,本题范围比较小,因此不用离散化,直接区间建树,节点维护4个值,
Len:区间内部被覆盖一次以上的长度
S:区间内被完全覆盖的次数
这个是常规操作。
然后维护区间内部,连续区间(每个之间是隔离的)的个数
然后两个lc,rc,代表区间左端点和右端点是否在连续区间内(合并区间的时候有用)
这样就行了。
然后考虑子节点往上pushup的情况,
首先区间被完全填满,那么len等于区间长度,lc,rc,num都是1。
如果到叶节点,都是0
否则,len,rc,lc,的长度由两个儿子节点提供,需要注意的是,num的情况是由两个儿子提供,如果左儿子的右边界和右儿子的左边界都是在连续的区间中,那么这个区间会被合成为1个区间,从而个数需要减1.
最后常规的操作即可。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N = ;
const int X = ;
const int inf = <<;
inline int L(int r){return r<<;};
inline int R(int r){return r<<|;};
inline int MID(int l,int r){return (l+r)>>;};
struct Edge{
int l,r;
int h;
int f;
}line[N*];
struct node{
int l,r,len,s,num;
//num这个区间有多少不连续的线段
bool lc,rc;//区间左右端点是否被覆盖
}tree[X<<];
bool cmp(Edge a,Edge b)
{
return a.h<b.h;
}
void pushup(int root)
{
if (tree[root].s){
tree[root].len=tree[root].r-tree[root].l+;//没有离散化
tree[root].rc=tree[root].lc=;
tree[root].num=;
}else if (tree[root].l == tree[root].r){
tree[root].len=;
tree[root].lc=tree[root].rc=;
tree[root].num=;
}else{
tree[root].len=tree[L(root)].len+tree[R(root)].len;
tree[root].lc=tree[L(root)].lc;
tree[root].rc=tree[R(root)].rc;
tree[root].num=tree[L(root)].num+tree[R(root)].num-(tree[L(root)].rc && tree[R(root)].lc);
}
}
void buildtree(int root,int l,int r){
tree[root].l=l;
tree[root].r=r;
tree[root].s=tree[root].len=;
tree[root].lc = tree[root].rc = tree[root].num = ;
if (l==r){
return ;
}
int mid = MID(l,r);
buildtree(L(root),l,mid);
buildtree(R(root),mid+,r);
}
void update(int root,int ul,int ur,int v)
{
int l=tree[root].l;
int r=tree[root].r;
//cout<<root<<l<<" "<<r<<" "<<ul<<" "<<ur<<endl;
if (ul==l && ur==r)
{
tree[root].s+=v;
pushup(root);
return;
}
int mid = MID(l,r);
if (ur<=mid)update(L(root),ul,ur,v);
else if(ul>mid)update(R(root),ul,ur,v);
else{
update(L(root),ul,mid,v);
update(R(root),mid+,ur,v);
}
pushup(root);
}
int main(){
int n;
while(scanf("%d",&n)!=EOF){
int x1,x2,y1,y2,mx=-inf,mn=inf;
int tot=;
for (int i=;i<n;i++){
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
mx=max(mx,max(x1,x2));
mn=min(mn,min(x1,x2));
line[tot].l=x1;
line[tot].r=x2;
line[tot].h=y1;
line[tot++].f=;
line[tot].l=x1;
line[tot].r=x2;
line[tot].h=y2;
line[tot++].f=-;
}
sort(line,line+tot,cmp);
int ans=;
int last=;
buildtree(,mn,mx-);
// cout<<"ss"<<endl;
for (int i=;i<tot;i++)
{
// cout<<line[0].l<<" "<<line[0].r<<endl;
update(,line[i].l,line[i].r-,line[i].f);
ans+=abs(tree[].len-last);
ans+=(line[i+].h-line[i].h)**tree[].num;
last=tree[].len;
}
printf("%d\n",ans);
}
return ;
}
#include<iostream>#include<algorithm>#include<stdio.h>#include<string.h>usingnamespacestd; constint N = 5007; constint X = 20007; constint inf = 1<<29; inline int L(int r){return r<<1;}; inline int R(int r){return r<<1|1;}; inline int MID(int l,int r){return (l+r)>>1;}; struct Edge{ int l,r; int h; int f; }line[N*2]; struct node{ int l,r,len,s,num; //num这个区间有多少不连续的线段bool lc,rc;//区间左右端点是否被覆盖 }tree[X<<2]; bool cmp(Edge a,Edge b) { return a.h<b.h; } void pushup(int root) { if (tree[root].s){ tree[root].len=tree[root].r-tree[root].l+1;//没有离散化 tree[root].rc=tree[root].lc=1; tree[root].num=1; }elseif (tree[root].l == tree[root].r){ tree[root].len=0; tree[root].lc=tree[root].rc=0; tree[root].num=0; }else{ tree[root].len=tree[L(root)].len+tree[R(root)].len; tree[root].lc=tree[L(root)].lc; tree[root].rc=tree[R(root)].rc; tree[root].num=tree[L(root)].num+tree[R(root)].num-(tree[L(root)].rc && tree[R(root)].lc); } } void buildtree(int root,int l,int r){ tree[root].l=l; tree[root].r=r; tree[root].s=tree[root].len=0; tree[root].lc = tree[root].rc = tree[root].num = 0; if (l==r){ return ; } int mid = MID(l,r); buildtree(L(root),l,mid); buildtree(R(root),mid+1,r); } void update(int root,int ul,int ur,int v) { int l=tree[root].l; int r=tree[root].r; //cout<<root<<l<<" "<<r<<" "<<ul<<" "<<ur<<endl;if (ul==l && ur==r) { tree[root].s+=v; pushup(root); return; } int mid = MID(l,r); if (ur<=mid)update(L(root),ul,ur,v); elseif(ul>mid)update(R(root),ul,ur,v); else{ update(L(root),ul,mid,v); update(R(root),mid+1,ur,v); } pushup(root); } int main(){ int n; while(scanf("%d",&n)!=EOF){ int x1,x2,y1,y2,mx=-inf,mn=inf; int tot=0; for (int i=0;i<n;i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); mx=max(mx,max(x1,x2)); mn=min(mn,min(x1,x2)); line[tot].l=x1; line[tot].r=x2; line[tot].h=y1; line[tot++].f=1; line[tot].l=x1; line[tot].r=x2; line[tot].h=y2; line[tot++].f=-1; } sort(line,line+tot,cmp); int ans=0; int last=0; buildtree(1,mn,mx-1); // cout<<"ss"<<endl;for (int i=0;i<tot;i++) { // cout<<line[0].l<<" "<<line[0].r<<endl; update(1,line[i].l,line[i].r-1,line[i].f); ans+=abs(tree[1].len-last); ans+=(line[i+1].h-line[i].h)*2*tree[1].num; last=tree[1].len; } printf("%d\n",ans); } return0; }
POJ - 1177 线段树的更多相关文章
- Picture POJ - 1177 线段树+离散化+扫描线 求交叉图像周长
参考 https://www.cnblogs.com/null00/archive/2012/04/22/2464876.html #include <stdio.h> #include ...
- poj 2886 线段树+反素数
Who Gets the Most Candies? Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 12744 Acc ...
- poj 3468(线段树)
http://poj.org/problem?id=3468 题意:给n个数字,从A1 …………An m次命令,Q是查询,查询a到b的区间和,c是更新,从a到b每个值都增加x.思路:这是一个很明显的线 ...
- POJ——3264线段树
题目: 输入两个数(m,n),m表示牛的头数,n表示查询的个数.查询时输入两个数(x,y),表示查询范围的起始值和终止值,查询结果是,这个区间内牛重量的最大值减去牛重量的最小值,数量级为1000,00 ...
- POJ 2828 线段树(想法)
Buy Tickets Time Limit: 4000MS Memory Limit: 65536K Total Submissions: 15422 Accepted: 7684 Desc ...
- poj 2828 线段树
http://poj.org/problem?id=2828 学到的思维: 1.变化的或者后来的优先影响前面的,那么从最后一个往前看,最后一个就成了 确定的, 而且后来的也能够确定----假设从前往后 ...
- poj 2886 (线段树+反素数打表) Who Gets the Most Candies?
http://poj.org/problem?id=2886 一群孩子从编号1到n按顺时针的方向围成一个圆,每个孩子手中卡片上有一个数字,首先是编号为k的孩子出去,如果他手上的数字m是正数,那么从他左 ...
- poj 2828(线段树 逆向思考) 插队是不好的行为
http://poj.org/problem?id=2828 插队问题,n个人,下面n行每行a,b表示这个人插在第a个人的后面和这个人的编号为b,最后输出队伍的情况 涉及到节点的问题可以用到线段树,这 ...
- poj 2528(线段树+离散化) 市长的海报
http://poj.org/problem?id=2528 题目大意是市长竞选要贴海报,给出墙的长度和依次张贴的海报的长度区间(参考题目给的图),问最后你能看见的海报有几张 就是有的先贴的海报可能会 ...
随机推荐
- c# 采用datatable 快速导入数据至MSSQL的方法分享
转自:http://www.maomao365.com/?p=5613 摘要:下文讲述使用c#代码快速将dataTable导入至mssql数据库的方法 实现思路:需要将datatable调整为同目标表 ...
- linux (fedora 28) 制作启动U盘,启动盘
最近需要安装一款Linux, 由于使用的计算机系统为 fedora 28, 所以只能在linux 制作U盘 使用 df 或者 fdisk -l 查看 U盘文件: Disk /dev/sdb: byte ...
- SpringCloud之初识Feign ----- 分布式负载自动拼接请求的URL
在前面的学习中,我们使用了Ribbon的负载均衡功能,大大简化了远程调用时的代码: String baseUrl = "http://user-service/user/"; Us ...
- [Java] SpringMVC工作原理之三:ViewResolver
一.ViewResolver 根据视图的名称将其解析为 View 类型的视图,如通过 ModelAndView 中的视图名称将其解析成 View,View 是用来渲染页面的,也就是将 Model 填入 ...
- Linux的基础命令
-shutdown –h now(root用户才有效) 立即关机 -shutdown –r now(root用户才有效) 立即重新启动计算机 -reboot ...
- linux 软连接创建 压缩解压缩 linux的dns服务相关
linux软连接创建 注意用绝对路径,语法如下 ln -s 目标文件绝对路径 软连接名字绝对路径 ln -s /小护士.txt /tmp/hs.txt 修改linux的PS1变量,命令提示符变量 PS ...
- centos7下安装docker(20.docker swarm start)
从主机的层面来看,docker swarm管理的是docker host集群. 什么是集群? 服务器集群由一组网络上相互连接的服务器组成,他们一起协同工作. 一个集群和一堆服务器的显著区别是: 集 ...
- 关于 chrome canary X64 在 win7 64bit 下面缺少openvr_api.dll的解决方法
在github上下载openvr_api.dll放到chrome的安装目录下就可以. 其实放到系统目录下最好,以后其他程序要使用的时候也能使用的到. https://github.com/ValveS ...
- 强化学习(三)—— 时序差分法(SARSA和Q-Learning)
1.时序差分法基本概念 虽然蒙特卡洛方法可以在不知道状态转移概率矩阵的前提下,灵活地求解强化学习问题,但是蒙特卡洛方法需要所有的采样序列都是完整的状态序列.如果我们没有完整的状态序列就无法用蒙特卡洛方 ...
- 11g SPA (sql Performance Analyze) 进行升级测试
注;转自http://ju.outofmemory.cn/entry/77139 11G的新特性SPA(SQL Performance Analyze)现在被广泛的应用到升级和迁移的场景.当然还有一些 ...