线段树 + 扫描线加深详解

在线段树中的扫描线主要是解决矩形面积以及周长问题,比如下图


让你求解所有矩形覆盖的面积和,或者是周长和,如果用寻常的方法,非常之麻烦,而且效率也不高,这里就会用到线段树的扫描线

扫描线应对方案:

由于题目提供的矩形比较多,坐标也很大,所以坐标需要离散化,可以按照题目要求或者自己的喜好,离散横坐标或者纵坐标都可以,这里讲的都是离散横坐标,不离散纵坐标

假设有一条扫描线,从下往上(从上往下)扫描过整个多边形叠加的区域。如果是竖直方向上扫描,则是离散化横坐标,如果是水平方向右扫描,

在进行扫描前,要保存好所有矩形的上边和下边,并且按照它们所处的高度进行排序,另外我们给上边赋值为-1,给上边赋值为1,接着我们用一个结构体来保存所有矩形的上边和下边,其中还有两条边不用管,因为他们已经包括在了高度中,高度差便能得到边的长度。

struct seg {
double l,r,h;//l代表着一条边左边端点的横坐标,r表示一条边右边端点的横坐标,h表示这条边所在的高度

int s;//表示他的值,即我们在前面讲的给边赋值1或者-1
seg() {}
seg(double l,double r,double h,int s):l(l),r(r),h(h),s(s) {}
bool operator < (const seg & object) const {//按照高度排序
return h < object.h;
}
}

然后将扫描线从下往上扫描,每遇到一条上边或者下边就停下来,将这条线段加入到总区间上,下边赋值是1,扫描到下边的话相当于往总区间插入一条线段,上边为-1,扫描到上边相当于在总区间删除一条线段(也可以理解为当插入1的时候我们的扫描线在一个矩形中,当插入-1的时候证明我们的扫描线离开了一个矩形的区域,如此可以知道,区间不会出现负数的情况,因为永远都是下边1 >= 上边-1,他们相加是永远大于等于零的)

然后用下一条边的高度减去当前这条边的高度,乘上总区间被覆盖的长度,就能得到最终的面积

到此,希望大家可以做一做这道题目,理解一下大概http://blog.csdn.net/qq_18661257/article/details/47622677

这道题目说明一下,pushup(rt, l, r)的作用以及其中判断的来由。

  1. void pushup(int rt,int l,int r) {  
  2.     if (Col[rt]) Sum[rt] = X[r+1] - X[l];//利用[ , ),这个区间性质,左闭右开  
  3.     else if (l == r) Sum[rt] = 0;  
  4.     else Sum[rt] = Sum[rt<<1] + Sum[rt<<1|1];  
  5. }  
如果当前的位置为叶子节点,Col[rt] == 1话证明被完全覆盖了,进行赋值(这里涉及到了离散化,建议读者可以学习离散化的知识),如果为Col[rt] == 0,这个叶子节点一定为0

如果当前位置不是叶子节点那么我们就要小心一点了,Col[rt] == 1话证明被完全覆盖了,如果Col[rt] == 0话,我们能说他没有被覆盖吗,不能,只能说没有被完全覆盖,为什么呢,因为他的子节点可能被完全覆盖了,但是父亲节点没有覆盖,那么怎么完全的不漏的得到这个区间被完全覆盖的区间大小呢,可以直接通过子节点覆盖的值

Sum[rt] = Sum[rt << 1] + Sum[rt << 1|1]得到rt这个位置总覆盖区间


好,如果我现在要求被覆盖两次以及以上的区间的面积怎么做,图形如下:


那么我们就要多增加一个数组用来记录覆盖了两次或者两次以上的区间大小。

Sum[rt]表示覆盖了一次的区间

Sum2[rt]表示覆盖了两次的区间

最开始跟上面的没有什么区别

如果Col[rt] >= 2的话,很明显这个区间一定都被覆盖了至少是两次

如果Col[rt] == 1的话,这里大家可以思考一下,直接从Sum2[rt] = Sum[rt << 1] + Sum[rt << 1|1]

这里为什么是从子节点传上来呢,如果Col[rt] == 1的话,证明此时这个位置已经被覆盖了一次,如果子节点也被覆盖了一次那不就是子节点被覆盖了两次吗,如此直接传给父亲节点,表示这个区间被覆盖了两次的答案

如此,如果Col[rt] == 0的话,证明这个地方没有被覆盖一次,那么我们只有从子节点将覆盖了两次的结果传来了。

Sum2[rt] = Sum2[rt << 1] + Sum2[rt << 1|1];

  1. void pushup(int rt,int l,int r) {  
  2.     if(Col[rt]) Sum[rt] = X[r + 1] - X[l];  
  3.     else if(l == r) Sum[rt] = 0;  
  4.     else Sum[rt] = Sum[rt << 1] + Sum[rt << 1|1];  
  5.   
  6.     if(Col[rt] >= 2) Sum2[rt] = X[r + 1] - X[l];  
  7.     else if(l == r) Sum2[rt] = 0;  
  8.     else if(Col[rt] == 1) Sum2[rt] = Sum[rt << 1] + Sum[rt << 1|1];  
  9.     else if(Col[rt] == 0) Sum2[rt] = Sum2[rt << 1] + Sum2[rt << 1|1];  
  10. }  

提供模板出处:http://blog.csdn.net/qq_18661257/article/details/47658101

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值