【笔记】传统势能线段树

1 引入

传统线段树能够通过打标记实现区间修改的条件有两个:

  1. 能够快速处理标记对区间询问结果的影响;
  2. 能够快速实现标记的合并。

有的区间修改不满足上面两个条件。

但存在一些奇妙的性质,使得序列每个元素被修改的次数有一个上限。

如果我们保证每暴力 O(logn) 修改一次的时候都能修改到一个值(也就是在一个数的修改次数达到上界的时候不再修改她),那么就可以均摊 O(nlog×修改上限) 解决。

具体到实现,可以考虑在线段树每个节点上记录一个东西(需要依题而定),用来表达对应区间内是否每个元素都达到修改次数上限。区间修改时暴力递归到叶子节点,如果途中遇到一个节点,这个节点的对应区间内每个元素都达到修改次数上限则在这个节点直接 return 掉。


2 关于势能在时间复杂度计算上的应用

首先定义一个势能函数初始值就是每个元素至多被修改的次数之和。

每次操作如果势能都是不增的,那么整体的复杂度就不会超过初始势能函数的值。


3 应用

p.s. 下面都是修改操作,查询操作应该是传统线段树能做的她都能做(?

3.1 区间取模

显然每次取模每次都让原数至少减少一半,修改上限就是 logV

具体到实现,我们维护每个区间的最大值,如果区间最大小于模数,那么就可以不用递归下去了。


3.2 区间开方

3.2.1 Solution1 维护是不是全 0/1

首先,对于任意数开方,可以在至多 loglogn 次内变为 0/1

why? 其实也很好理解,因为开方一次相当于 12 方。所以就是问 logn 的最多能除 12 几次,所以就是 loglogn

与 3.1 类似,还是维护区间最大值就行了。


3.2.2 Solution2 维护是不是全相等

其实这道题还有一种维护方式。

直接维护区间最大值、最小值,以此来看区间是不是全部相同,如果是就直接打区间覆盖的标记。


3.2.3 区间开方,区间加法

现在我们先用势能去理解一下上面两种维护方式。

对于第一个,对于每个节点其势能函数应该是该区间还不是 0/1 的个数。

对于第二个,对于每个节点其势能函数应该是该区间不同的元素个数。

现在加上区间加之后,我们发现第一个势能函数会增加了,时间复杂度假了,但是第二个并不会!所以说,如果有区间加就只能用第二种维护方式。

但是还没完,对于这样的数据:3434,开方为 1212,再加 2 为 3434,这样就会循环。

解决办法是直接特判一下这种情况。

对于 max=min 的情况,打区间覆盖标记。

对于 max=min+1 的情况,相当于区间减 maxmax,打区间减标记。


3.3 区间除法

与取模类似每次都让原数至少减少一半,修改上限同样也是 logV

注意,这里除法的结果是要数学上的向下取整的话,如果是正数最后会停在 0,反之会停在 1

如果是用 3.2.1 的维护方式,需要分别维护区间是不是全 0/1,有点麻烦。

下面还是考虑 3.2.2。

还是特判 maxmin=1 的区间.

对于 max/d=min/d 的情况,打区间覆盖标记。

对于 max/d=min/d+1 的情况,相当于区间减 maxmax/d,打区间减标记。


4 总结

对于常见的数学运算(比如取模就不算),一般都可以考虑 3.2.2 维护方式,即:

考虑维护 min,max,maxmin,然后边界情况就可以打标记,非边界递归两边。

posted @   CloudWings  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示