zoukankan      html  css  js  c++  java
  • 关于扫描线的一点理解

    关于扫描线的一点理解

    扫描线本质上是一个只需上传的线段树.

    int cover[N]; 	// 记录线段是否被完全覆盖的次数.
    int length[N];	// 记录线段被覆盖过的长度
    

    我们在进行扫描时,线段树的结点是什么?

    void update(int l,int r,int rt,int ul,int ur,int degree){
    	if(ul <= l && ur >= r){
            cover[rt] += degree;
            mapToLength(l,r,rt);
        }else{
            if(l + 1 == r){
                return;
            }
            int mid = l+r>>1;
            if(ul <= mid){
                update(l,mid,rt<<1,ul,ur,degree);
            }
            if(ur > mid){
                update(mid,r,rt<<1|1,ul,ur,degree);
            }
            mapToLength(l,r,rt);
        }
    }
    

    可以看出,线段树所直接维护的其实是cover[N]数组,而非length[N]数组.

    但是length[N]必须由cover[N]推导得到,我们将这个步骤称为cover[N]数组映射到length[N]数组

    void push_up(int l,int r,int rt){
        if(cover[rt]){
            length[rt] = ordered[r] - ordered[l];
        }else{
         	if(l+1==r){
                length[rt] = 0;
            }else{
                length[rt] = length[rt<<1] + length[rt<<1|1];
            }
        }
    }
    

    因此,我们所有的update操作都只需要上传更新,没必要下传更新.

    为什么可以这么做呢?

    • 为什么更新需要上传?

      因为我们最终需要根据length[1]计算扫描线长度.

      每一次cover[rt]的更新都会执行一次push_up将这个更新操作映射到length[i],而在这个映射操作中就包含了对于length[rt]的上传. 同时,update又是一个递归的过程,因此最终每一个update操作都会更新路径上的length结点,最终保持length[1]的正确性.

    • 为什么更新不需要下传?

      要回答这个问题,我们首先考虑为什么普通的线段树需要下传更新.常规的线段树题目,往往会询问一段子区间的值,比如先更新[1,10]区间的值,然后统计[6,10]区间的值. 如果我们只对[1,10]进行更新,而不将这个更新下传到[6,10]区间,那么在询问[6,10]就得不到正确的结果.因此,下传操作主要是应对子区间的查询.而本题,扫描线所需要统计的值是整个扫描线的长度,即只用到了根结点的值,因此每一个更新操作只需要上传即可,这个刚刚讲过,push_up操作已经完成了更新的上传.

    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    语言只是个工具
    最近学到的一点东西
    iBeacon开发
    马上着手开发Mac应用程序
    Text Kit入门
    Text Kit进阶
    Web Notification
    Objective-C异步编程
    Clang Language Extensions
    黑客与画家
  • 原文地址:https://www.cnblogs.com/popodynasty/p/13954362.html
Copyright © 2011-2022 走看看