首页 > 代码库 > 线段树 + 扫描线加深具体解释

线段树 + 扫描线加深具体解释

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

技术分享
技术分享
技术分享
技术分享
技术分享

让你求解全部矩形覆盖的面积和,或者是周长和,假设用平常的方法,很之麻烦。并且效率也不高。这里就会用到线段树的扫描线

扫描线应对方案:

因为题目提供的矩形比較多。坐标也非常大。所以坐标须要离散化,能够依照题目要求或者自己的喜好,离散横坐标或者纵坐标都能够,这里讲的都是离散横坐标,不离散纵坐标

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

在进行扫描前。要保存好全部矩形的上边和下边。而且依照它们所处的高度进行排序,另外我们给上边赋值为-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

线段树 + 扫描线加深具体解释