普通的线段树在做区间修改时依赖懒标记(lazy tag),当我们从一个点向下访问时,需要将标记 pushdown。能否避免如此多的 pushdown 操作呢?这时需要用到标记永久化技巧。
我们要做的就是将 lazy tag 永久地留在当前的结点,这时子树中的所有结点都不会被这个 tag 所影响。因此,子树中询问的最大值 = 实际最大值 - tag。当我们想得到正确答案时,只要将子树返回的最大值加上当前 tag 即可。
复杂度分析:由于标记不会下放,但如果有两个标记落在了一个结点上,我们不会分别存储这两个标记,而是加起来合成一个标记($(+2) + (+3) = (+5)$)。因此,每个结点最多只有一个标记。询问时最多考虑 $\log n$ 个标记,复杂度和普通线段树相同 $O(\log n)$。
| |
操作总数 $1 \leq n \leq 10^5$,$1 \leq k, x_0, x_1 \leq 39989$,$1 \leq y_0, y_1 \leq 10^9$。
注意:当线段垂直于 $y$ 轴时,如果按照一般的式子计算,会出现除以零的情况。假设线段两端点分别为 $(x,y_0)$ 和 $(x,y_1)$,$y_0<y_1$,则插入定义域为 $[x,x]$ 的一次函数 $f(x)=0\cdot x+y_1$。
如图,按新线段 $f$ 取值是否大于原标记 $g$,我们可以把当前区间分为两个子区间。其中 肯定有一个子区间被左区间或右区间完全包含,也就是说,在两条线段中,肯定有一条线段,只可能成为左区间的答案,或者只可能成为右区间的答案。我们用这条线段递归更新对应子树,用另一条线段作为懒标记更新整个区间,这就保证了递归下传的复杂度。当一条线段只可能成为左或右区间的答案时,才会被下传,所以不用担心漏掉某些线段。
除了这两种情况之外,还有一种情况是 $f$ 和 $g$ 刚好交于中点,在程序实现时可以归入 $f$ 不如 $g$ 优的情况,结果会往 $f$ 更优的一个端点进行递归下传。
复杂度分析:查询的时间复杂度显然为 $O(\log n)$,而插入过程中,我们需要将原线段拆分到 $O(\log n)$ 个区间中,对于每个区间,我们又需要花费 $O(\log n)$ 的时间递归下传,从而插入过程的时间复杂度为 $O(\log^2 n)$。
| |
普通的线段树在做区间修改时依赖懒标记(lazy tag),当我们从一个点向下访问时,需要将标记 pushdown。能否避免如此多的 pushdown 操作呢?这时需要用到标记永久化技巧。
我们要做的就是将 lazy tag 永久地留在当前的结点,这时子树中的所有结点都不会被这个 tag 所影响。因此,子树中询问的最大值 = 实际最大值 - tag。当我们想得到正确答案时,只要将子树返回的最大值加上当前 tag 即可。
复杂度分析:由于标记不会下放,但如果有两个标记落在了一个结点上,我们不会分别存储这两个标记,而是加起来合成一个标记($(+2) + (+3) = (+5)$)。因此,每个结点最多只有一个标记。询问时最多考虑 $\log n$ 个标记,复杂度和普通线段树相同 $O(\log n)$。
| |
操作总数 $1 \leq n \leq 10^5$,$1 \leq k, x_0, x_1 \leq 39989$,$1 \leq y_0, y_1 \leq 10^9$。
注意:当线段垂直于 $y$ 轴时,如果按照一般的式子计算,会出现除以零的情况。假设线段两端点分别为 $(x,y_0)$ 和 $(x,y_1)$,$y_0<y_1$,则插入定义域为 $[x,x]$ 的一次函数 $f(x)=0\cdot x+y_1$。
如图,按新线段 $f$ 取值是否大于原标记 $g$,我们可以把当前区间分为两个子区间。其中 肯定有一个子区间被左区间或右区间完全包含,也就是说,在两条线段中,肯定有一条线段,只可能成为左区间的答案,或者只可能成为右区间的答案。我们用这条线段递归更新对应子树,用另一条线段作为懒标记更新整个区间,这就保证了递归下传的复杂度。当一条线段只可能成为左或右区间的答案时,才会被下传,所以不用担心漏掉某些线段。
除了这两种情况之外,还有一种情况是 $f$ 和 $g$ 刚好交于中点,在程序实现时可以归入 $f$ 不如 $g$ 优的情况,结果会往 $f$ 更优的一个端点进行递归下传。
复杂度分析:查询的时间复杂度显然为 $O(\log n)$,而插入过程中,我们需要将原线段拆分到 $O(\log n)$ 个区间中,对于每个区间,我们又需要花费 $O(\log n)$ 的时间递归下传,从而插入过程的时间复杂度为 $O(\log^2 n)$。
| |