线段树 (扫描线)

  这里用HDU的1542题作为例子,一个经典的扫描线题目,计算矩形并的和。

  首先介绍扫描线,就是一根假想的线,从左到右的一条竖线扫描过去。

  扫描线可以用来填充多边形,具体请看 http://blog.csdn.net/orbit/article/details/7368996  写的很好。

  然后就是扫描线在这个题里的应用。

  

  计算这两个矩形的面积等价于计算红色,绿色,蓝色三块的面积的和。

  所以说从左到右的扫描线扫描的时候,线段树维护的是区间内被覆盖的总长度,然后乘以两条扫描线之间的距离,就是一块的面积,然后更新线段树,以此论推,得到的就是总面积了。

  不过这个题要注意三个地方:

  1,离散化,每条边的坐标是浮点数,要进行离散化,要记得去重。

  2,就是区间覆盖的长度计算问题,这里我纠结了老久,首先如果按照点来覆盖的话对区间2,8覆盖其实是对1,5和6,10进行覆盖(如果线段树左右子树的区间是1,5和6,10的话,那么5和6中间那一块就没有被计算上,就出问题了。。。

  所以说不能按照点来覆盖,应该把每个点当作是表示这个点到下一个点之间的这条线段,这样就不会漏掉中间那一个了。

  如果是表示线段的话,那么如果有10个点,那么有9条线段,所以线段树是1到9(而不是10),每次计算长度的时候记得右边要加一。

  3,就是线段树的更新问题,每加一条线段或者减一条线段的话,并不是简单的覆盖就好,而是应该记录这个区间被覆盖了几次,直到减到零的时候才是没有线段在覆盖这个区间。这样的话每加一条线段就是对线段的区间+1,去一条线段就是-1。

  不过这样的话pushDown就不好用了,因为如果把一个节点的COL值推到下面两个之后,之后如果再覆盖,这个节点的COL变成-1了,那就不好算这个节点维护的值了。所以根据杭电大神的写法,不用pushDown,而是在计算pushUP的时候,如果这个节点已经被覆盖为大于0的就直接计算(因为这个节点表示的整个区间都被覆盖了,直接右面减去左面就是了。),否则才求助于下面的两个节点。而且,询问只是询问节点1的值,所以不会出错。

 

  近来又看到了一个大神的线段树写法,他用了pushDown,但是也对pushUP进行了修改,从而保证不会让某一个节点的值为零,虽说速度不会快,但是可扩展性要高很多,因为可以让有的矩形第一条边为-1,第二条边为1(HDU  3265)这个题。

  修改的代码我在HDU  3265和HDU  1542中都贴出来了。

 

  差不多就这三个了,水平有限,敬请谅解。

 

例题:

  HDU  1542  题解请单击

  HDU  1828  题解请单击

  HDU  3265  题解请单击

posted @ 2015-01-09 23:48 WhyWhy。 阅读(...) 评论(...) 编辑 收藏