ACM-线段树扫描线总结
扫描线的基础概念可以看这几篇文章
http://blog.csdn.net/xingyeyongheng/article/details/8927732
http://www.cnblogs.com/scau20110726/archive/2013/03/21/2972808.html
在这里讲一下应用
1.求矩形的面积并
以hdu 1542为例:
给出n个矩阵的对角坐标,求矩阵的面积并。
这里需要注意这么几点:
- 离散化
因为数据跨度很大,所以肯定要将数据离散化,保存在hash数组中。但要小心离散化本身的坑。
离散化的坑,就是在表示线段树时,我们会把1-4划分为1-2和3-4,如果覆盖1-2和3-4就等同于把整个线段覆盖了,但其实不对,因为2和3中间也是有值的。
下面这题也遇到这个坑,解决方法是:在update时读入r-1,计算时使用r+1
- 水平扫描的话,hash的是y坐标,反之亦然
- hash最好要去重
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std; #define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!\n")
#define MAXN 200+5
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue
#define INF 0x3f3f3f3f #define ls (rt<<1)
#define rs (rt<<1|1) int n,m; double hh[MAXN],col[MAXN<<],len[MAXN<<]; struct node
{
double l,r,x,c;
node(){}
node(double a,double b,double c,double d):l(a),r(b),x(c),c(d){}
bool operator < (const node &b) const
{
return x<b.x;
}
}a[MAXN]; void PushUp(int rt,int l,int r)
{
if(col[rt])
{
len[rt] = hh[r+] - hh[l];
}
else if(l==r) len[rt] = ;
else
{
len[rt] = len[ls]+len[rs];
}
} void update(int val,int L,int R,int l,int r,int rt)
{
if(L<=l && r<=R)
{
col[rt] += val;
PushUp(rt,l,r);
return;
}
int mid = (l+r)>>;
if(L <= mid) update(val,L,R,l,mid,ls);
if(R > mid) update(val,L,R,mid+,r,rs);
PushUp(rt,l,r);
} int main()
{
int i,j,k,t,kase=;
while(~sf("%d",&n) && n)
{
int v=;
double sum = ;
for(i=;i<n;i++)
{
double x1,y1,x2,y2;
sf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
hh[v] = y1;
a[v++] = node(y1,y2,x1,);
hh[v] = y2;
a[v++] = node(y1,y2,x2,-);
}
sort(hh,hh+v);
sort(a,a+v);
int d = ;
for(i=;i<v;i++)
{
if(hh[i]!=hh[i-]) hh[d++] = hh[i];
}
mem(len,);
mem(col,);
for(i=;i<v-;i++)
{
int l = lower_bound(hh,hh+d,a[i].l)-hh;
int r = lower_bound(hh,hh+d,a[i].r)-hh-;
update(a[i].c,l,r,,d-,);
sum+=len[]*(a[i+].x-a[i].x);
//pf("%lf %lf\n",sum,len[1]);
}
pf("Test case #%d\nTotal explored area: %.2lf\n\n",kase++,sum);
} return ;
}
2.求矩形周长并
以hdu 1828为例:
http://blog.csdn.net/xingyeyongheng/article/details/8931410
http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018687.html
http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018702.html
三篇很不错的文章,基础概念可以看这几个
从面积变成了周长,方法其实是差不多的。解决这个问题有两种思路:
- 对横线,竖线分别做一次处理。分别求出有效覆盖长度,两者之和就是结果。
把矩形分成横线和竖线去处理,可知是完全相同的操作,我们来讲下怎么算出横线部分,竖线部分就是照搬即可。
将横线保存在一个表中,按横线所处的竖直位置排序(升序),另外每条横线带一个标记值,原矩形的下线为1,上线为-1(对应过去就是插入线段和删除线段)
从低到高扫描横线,没扫到一条横线就能计算出一部分横线值。计算方法是算出现在总区间的被覆盖的长度,然后求出与上一次的总区间的覆盖长度的差(即相减求绝对值),因为每次添加了一条线段,如果没有没有使总区间覆盖长度发生变化,说明这条线段其实在多边形的内部,被覆盖掉了,不能计算,只要能引起总区间长度发生变化的,说明该线段不被覆盖不被包含,要计算
而竖线部分的做法是一样的,把竖线保存在一个表中,按水平位置排序(升序),每条横线带一个标记值,原矩形的左线为1,右线为-1,然后同样地操作
- 通过记录覆盖的左右端点,以及数量信息,判断是否覆盖,扫描一次的同时求出两线长度。
前面的工作是相同的,把横线保存在一个表中,按竖直位置排序,然后从下往上扫描所有横线,在这个方法中,每次扫描一条横线,都能计算出两不部分值,一部分是横线的,一部分是竖线的
而计算横线部分的方法和第一种方法是一样的,即求出【现在这次总区间被覆盖的长度和上一次总区间被覆盖的长度的差的绝对值】,另外怎么算竖线部分呢?首先我们要知道添加了这条横线后会有多少条竖线,答案是2*num,所以为什么要记录num呢,因为总区间被num条线段覆盖,那么必定有2*num的端点,这些端点其实就是连着竖线,那么这些竖线的长度是多少呢?
就是【下一条横线的高度-现在这条横线的高度】,只要把这个高度乘上2*num即可
我采用的是第二种方法,这里不需要离散化,代码如下:
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std; #define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!\n")
#define MAXN 20000+5
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue
#define INF 0x3f3f3f3f #define ls (rt<<1)
#define rs (rt<<1|1) int n,m; int col[MAXN<<],len[MAXN<<],lseg[MAXN<<],rseg[MAXN<<],segnum[MAXN<<]; struct node
{
int l,r,x,c;
node(){}
node(int a,int b,int c,int d):l(a),r(b),x(c),c(d){}
bool operator < (const node &b) const
{
return x<b.x || x==b.x && c>b.c;
}
}a[MAXN]; void PushUp(int rt,int l,int r)
{
if(col[rt])
{
len[rt] = r-l+;
segnum[rt]=;
lseg[rt] = rseg[rt] = ;
}
else if(l==r) len[rt] = segnum[rt] = lseg[rt] = rseg[rt] = ;
else
{
len[rt] = len[ls]+len[rs];
segnum[rt] = segnum[ls]+segnum[rs];
lseg[rt] = lseg[ls];
rseg[rt] = rseg[rs];
if(lseg[rs] && rseg[ls]) segnum[rt]-=;
}
} void update(int val,int L,int R,int l,int r,int rt)
{
if(L<=l && r<=R)
{
col[rt] += val;
PushUp(rt,l,r);
return;
}
int mid = (l+r)>>;
if(L <= mid) update(val,L,R,l,mid,ls);
if(R > mid) update(val,L,R,mid+,r,rs);
PushUp(rt,l,r);
} int main()
{
int i,j,k,t,kase=;
while(~sf("%d",&n) && n)
{
int left = ,right = -,v=,ans=,last=;
mem(len,);
mem(col,);
mem(segnum,);
mem(lseg,);
mem(rseg,);
for(i=;i<n;i++)
{
int x1,y1,x2,y2;
sf("%d%d%d%d",&x1,&y1,&x2,&y2);
a[v++] = node(y1,y2,x1,);
a[v++] = node(y1,y2,x2,-);
left = min(left,y1);
right = max(right,y2);
}
//pf("%d %d\n",left,right);
sort(a,a+v);
for(i=;i<v;i++)
{
if(a[i].l < a[i].r) update(a[i].c,a[i].l,a[i].r-,left,right,);
ans+= segnum[]*(a[i+].x-a[i].x);
ans+= abs(len[]-last);
last = len[];
}
pf("%d\n",ans);
} return ;
}
ACM-线段树扫描线总结的更多相关文章
- 2018牛客网暑假ACM多校训练赛(第四场)E Skyline 线段树 扫描线
原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-E.html 题目传送门 - https://www.no ...
- hdu 4052 线段树扫描线、奇特处理
Adding New Machine Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- HDU 1542 - Atlantis - [线段树+扫描线]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542 Time Limit: 2000/1000 MS (Java/Others) Memory Li ...
- 【Codeforces720D】Slalom 线段树 + 扫描线 (优化DP)
D. Slalom time limit per test:2 seconds memory limit per test:256 megabytes input:standard input out ...
- Codeforces VK CUP 2015 D. Closest Equals(线段树+扫描线)
题目链接:http://codeforces.com/contest/522/problem/D 题目大意: 给你一个长度为n的序列,然后有m次查询,每次查询输入一个区间[li,lj],对于每一个查 ...
- 【POJ-2482】Stars in your window 线段树 + 扫描线
Stars in Your Window Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11706 Accepted: ...
- HDU 4419 Colourful Rectangle --离散化+线段树扫描线
题意: 有三种颜色的矩形n个,不同颜色的矩形重叠会生成不同的颜色,总共有R,G,B,RG,RB,GB,RGB 7种颜色,问7种颜色每种颜色的面积. 解法: 很容易想到线段树扫描线求矩形面积并,但是如何 ...
- BZOJ-3228 棋盘控制 线段树+扫描线+鬼畜毒瘤
3228: [Sdoi2008]棋盘控制 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 23 Solved: 9 [Submit][Status][D ...
- BZOJ-3225 立方体覆盖 线段树+扫描线+乱搞
看数据范围像是个暴力,而且理论复杂度似乎可行,然后被卡了两个点...然后来了个乱搞的线段树+扫描线.. 3225: [Sdoi2008]立方体覆盖 Time Limit: 2 Sec Memory L ...
- hdu 5091(线段树+扫描线)
上海邀请赛的一道题目,看比赛时很多队伍水过去了,当时还想了好久却没有发现这题有什么水题的性质,原来是道成题. 最近学习了下线段树扫描线才发现确实是挺水的一道题. hdu5091 #include &l ...
随机推荐
- django中itsdangerous的用法
itsdangerous用来解决什么问题,为什么需要用到itsdangerous? 安装命令:pip install itsdangerous 有时候你想向不可信的环境发送一些数据,但如何安全完成这个 ...
- Java中的Date Time 与SQL Server 2005里的Datetime 之间的交互
Preface Environment:Platform: Windows XPLanguage: Java 1.5IDE: MyEclipse 6.0.1Database: SQL Server 2 ...
- oracle知识总结
Oracle 分页案例: SELECT * FROM(SELECT A.*, ROWNUM RN FROM (SELECT * FROM Table_name) AWHERE ROWNUM <= ...
- ThinkPHP U方法
方法1: {:U('User/Booking/bookingdetails')} 方法2: {:U('User/Booking/bookingdetails')}"+"&a ...
- shared_ptr智能指针
来自博客:https://www.cnblogs.com/lzpong/p/6188034.html 多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_p ...
- COCO2018 全景分割
全景分割是18年新推出的一个任务,它要求同时分割出目标和背景,也就是既有实例分割也有语义分割,用官方的话讲是朝着真实世界视觉系统的重要一步 如图所示,里面既有对天空,草地等stuff的分割,也有对目标 ...
- python---day14( 内置函数二)
内置函数二一:匿名函数 lambda函数 lambda 表示匿名函数,不需要用def 来申明. 语法: 函数名=lambda 参数:返回值 ----〉 案例:f=lambda n:n*n 例子01: ...
- 康奈尔大学CFD课程
先上链接:https://confluence.cornell.edu/display/SIMULATION/Home 如果不会FQ,可以去edx:https://courses.edx.org/co ...
- LOJ6519. 魔力环(莫比乌斯反演+生成函数)
题目链接 https://loj.ac/problem/6519 题解 这里给出的解法基于莫比乌斯反演.可以用群论计数的相关方法代替莫比乌斯反演,但两种方法的核心部分是一样的. 环计数的常见套路就是将 ...
- window 中安装 hadoop
win10上部署Hadoop-2.7.3——非Cygwin.非虚拟机 开始接触Hadoop,听人说一般都是在Lunix下部署Hadoop,但是本人Lunix不是很了解,所以Google以下如何在W ...