关于扫描线的一点理解

关于扫描线的一点理解

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

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 ----
原文地址:https://www.cnblogs.com/popodynasty/p/13954362.html