关于扫描线的一点理解
扫描线本质上是一个只需上传的线段树.
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
操作已经完成了更新的上传.