楼房重建

发布时间 2023-12-29 17:38:45作者: 最爱丁珰

这一道题目稍微转换一下题意之后就可以知道需要维护一个斜率单调递增的序列(下文把每个点的斜率称为权值)

所以动态维护权值单增序列可以像下面这么做

我们根据线段树“分而治之”的思想,用每一个节点维护相似的信息

维护什么呢?维护在这一个节点的代表区间中,以这一个节点所代表的区间的第一个点为开头的最大的权值不下降序列的长度,以及这个节点所代表的区间的权值的最大值

那么查询的时候直接输出根节点的序列长度即可

修改的时候,先点修改到最低的叶子节点,那么合并怎么合并呢?

在合并的时候,父节点肯定会继承左子节点的序列(即左子节点的序列是父节点序列的左半部分),问题是右节点怎么办呢?

这个时候,我们要解决的问题是:在右子节点中寻找第一个权值比左子节点最大权值还要大的位置,并从这个位置开始所得到的不下降序列长度

我们就将其作为一个函数,尝试进行分而治之,即解决在当前节点中寻找第一个权值比给定权值还要大的位置,并从这个位置开始所得到的不下降序列长度

如果右子节点的左子节点的最大值比左子节点的最大值小,那么右子节点的左子节点的所有房子一定看不到,所以我们就可以递归查找右子节点的右子节点

如果右子节点的左子节点的最大值比左子节点的最大值大,可以想一下应该怎么做(具体见代码,这个很容易看懂)

时间复杂度为\(O(log^2n)\)

我还想了另一种方法,也很经典

用一颗线段树维护两个值,一个是区间最大值,一个是区间和,这里的和定义为当前节点所代表区间的每个点的贡献和,而每个点的贡献要么是\(1\)要么是\(0\),当且仅当这个点能被看到的时候贡献为\(1\)

那么在修改一个点的时候,我们值讨论是增高的这种情况

如果增高的不够多(就是前面的最高的房子更高),那么更新一下区间最大值就好了,其他都不变

如果增高的足够多了,那么在修改最大值的同时,也要把这个点的贡献变为\(1\),然后二分查找这个点后面的点第一个比这个点的高度还要高的点,然后把这两个点中间的点的贡献全部修改为\(0\),每次查询就直接查询根节点的和即可,时间复杂度仍然是\(O(log^2n)\)