亚特兰蒂斯

这里主要是对代码的解释

首先是对蓝书的简单做法的代码解释

我们把每一个节点看做比较独立的一个个体,他的cnt表示他所代表的区间是否在他本身的影响下被完全覆盖,这里不太好描述,下面举一个例子

比如三号节点(代表区间为[4,6])的cnt>0表示这一段区间在他这里已经被完全覆盖了,cnt=0表示在他这里没有被完全覆盖

那么在实际中,一个单位区间如何判断他是否被覆盖?

就看从代表这个区间的叶子节点到根节点的路径上,是否存在一个节点的cnt>0

比如[4,4]这个区间,如果1,3,6,12这四个节点中有一个cnt>0那么就代表实际中,[4,4]这个区间被覆盖了

我们先不考虑长度,先单独考虑维护区间是否被覆盖,那么在这种情况下,当修改操作为+1时,线段树被划分成的若干个节点的cnt都加上1,从每一个叶子节点往上走,显然都是符合实际情况的,如果修改操作为-1,当线段树被划分成的若干个节点中的某一个节点的cnt变为0了,他所代表的这些单位区间往上走,如果某个单位区间在实际中还被覆盖,那么他一定是被之前的某一次+1操作在路径上的另一个节点覆盖的,由于操作成对出现,这个节点的-1操作肯定比+1操作后面出现,所以这个节点的cnt一定>0,所以也就不会遗漏答案,同样的分析,也不会重复计算

我们再来考虑长度,由于线段树是从下往上维护信息的,我们不妨用最暴力的方法来维护信息,假设每次修改完了之后我们都从每个单位区间向上走,走到某一个节点时,如果当前节点的cnt=0,就向上传递其子节点的len之和,如果cnt>0就传递这个区间长度,那么由我们以上分析,如果一个单位区间被记录了,那么他一定会在某一个节点(cnt>0)的时候被计算贡献,也就不会遗漏答案(这个时候其实线段树中除了根节点外的节点的len可能不是实际中的len,但是由于我们只关心根节点,根节点的len一定是实际的len,就无所谓)

我们在修改之后可以直接模拟这个过程,在某次修改中,没有被涉及的区间显然不用管,他们已经传递好了,对于涉及的区间,如果cnt被减为了0,那么我们假设从这个区间所代表的的所有单位区间向上走,由于这个区间的子树都没有被修改,所以我们不用真的走,这个区间的两个子节点的len值就是已经走完之后的len,我们直接上传两个子节点的len即可

然后我们考虑如果硬要用lazy标记怎么做

首先我们考虑简单一点的情况,我们先不管区间长度,我们只维护\(c\)数组

相当于就是简单的区间修改,区间查询最小值

t[p].dat表示\(p\)这个节点所代表区间的\(c\)的最小值,t[p].lazy表示\(p\)这个节点的lazy标记(自己已经改变了,但是子节点的dat和lazy还需要加上自己这个lazy的变化)

根据我们对lazy的理解,在任何一个时刻,某一个节点的dat可能不是真正的dat,必须要加上从这个节点到根节点的路径上所有节点的lazy值之和才是真正的dat;在修改函数或者查询函数走到某一个节点时,这个节点的dat和lazy是真实的

想一下我们的问题出现在哪里,如果我们现在要维护不为\(0\)\(c\)的长度总和,就要增加一个维度,t[p].len表示\(p\)这个节点所代表的的区间的真实长度和(注意,lazy是管dat的,就是说只有dat的值才可能不是真实的,我们不放认为在任意时刻,len的值都是真实的,下文尝试维护这个真实性)

那么我们在修改的时候,如果t[p].dat从\(1\)减到了\(0\),我们要怎么修改t[p].len呢?也许我们会认为有t[p].len=t[leftson].len+t[rightson].len,但实际上这是错误的,根据我们对lazy标记的理解,此时leftson和rightson的dat值并不是真实的(因为修改函数没有继续往下面递归了),也许我们会说,肯定都为\(0\)啊,的确,但是len呢?两者的len也是错误的。为什么?因为p的dat为\(0\),则说明\(p\)所代表的区间的某些\(c\)变成了\(0\),然而此时你的leftson和rightson的len却是两者的区间长度(因为两者的dat不为\(0\)),所以肯定不是t[p].len=t[leftson].len+t[rightson].len

上文的分析也说明了我们要修改t[p].len就要先修改\(p\)的左右儿子的len,然而我们在修改左右儿子的len时,要先修改左右儿子的左右儿子的len,然后以此类推,肯定超时

所以我们必须换一个思路,我们认为t[p].len表示\(p\)所代表的的区间的所有\(c\)的值为dat的\(c\)的长度和,比如

显然dat是2,那么为2的长度和为3,所以len就为3

如果任意时刻任意节点的len都是真实的,那么当我们递归到\(p\)这一个节点(\(p\)被完全覆盖,不会继续往下递归)的时候,t[p].len显然不变(而且\(p\)子树的所有节点的len都不变),我们只需要考虑如何改变\(p\)的父亲的len

假设我们现在回溯了到\(p\)的父亲了(我们假设已经走完了这个父亲的两个子节点,而且两个子节点的数据都已经维护好了),那么我们只需要判断这个父亲的dat(注意,此时这个dat是真实的)与两个儿子哪个的dat相同,那么父亲的len就是这个儿子的len(当然可能与两个都相同,这个时候父亲的len就是两个儿子的len的和)

由数学归纳法,我们可以知道我们维护的操作是正确的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值