势能线段树学习笔记

势能线段树


也叫吉司机线段树


用途

区间最值操作

给你一个长度为 n n n 的序列,有 Q Q Q 次操作,操作类型如下:

0 u v t :对于所有 i i i , $u \leq i \leq v $ , min ⁡ ( a i , t ) → a i \min(a_i,t) \to a_i min(ai,t)ai

1 u v :对于所有 i i i , $u \leq i \leq v $ , 输出最大的 a i a_i ai

2 u v :对于所有 i i i , $u \leq i \leq v $ , 输出的 a i a_i ai 的和。

对于 12 ,在学普通的线段树时就应该掌握,而他们也确实不是今天的主角。

看到 0 ,怎么维护?

这就要用到势能线段树了。

我们在线段树中维护区间的最大值,最大值个数, 严格次大值(一定小于最大值),当然,题目还要求总和,所以这个也要加上。

那我们如何更新呢?

0 出现时,我们进入线段树,会出现三种情况:

  1. 当一个区间的最大值小于等于 t t t 时,直接退出,因为该操作肯定不会对它造成影响;
  2. 当一个区间的最大值大于 t t t ,而严格次大值小于 t t t 时,我们只要拿最大值出来更新就可以了;
  3. 当一个区间的严格次大值大于等于 t t t 时,我们让它暴力扫下去,也就是让它的子区间去更新。

有人会疑惑了:“这怎么保证时间复杂度?”

确实,这是一个令人困惑的问题,但是可以仔细想一下:

我们将一个区间内不同数字的种类数叫做 势能 ,那么一整棵线段树上的势能总和最大为 n log ⁡ 2 n n\log_2{n} nlog2n ,而我们每做一次暴力修改,整体势能会至少减一,那么暴力执行最多 n log ⁡ 2 n n\log_2{n} nlog2n 次,时间复杂度也就是 O ( n log ⁡ 2 n ) O(n\log_2{n}) O(nlog2n)

这也是为什么它叫做势能线段树

不过,这是不带其他种类的修改的情况,带上修改后时间复杂度就变成了 O ( n log ⁡ 2 2 n ) O(n\log_2^2{n}) O(nlog22n)

这只是势能线段树的其中一种用途,叫做区间最值操作,除了最小值还有最大值,不过都是一样的。

区间历史最值

给你一个长度为 n n n 的序列,有 Q Q Q 次操作,操作类型如下:

0 u v t :对于所有 i i i , $u \leq i \leq v $ , a i a_i ai 加上 t t t

1 u v :对于所有 i i i , $u \leq i \leq v $ , 输出最大的 a i a_i ai​ ;

2 u v :对于所有 i i i , $u \leq i \leq v $ , 输出最大的 a i a_i ai 达到过的值;

那么我们又该怎么维护这个东东呢?

我们在线段树中维护区间的最大值 m x mx mx,历史最大值 h m x hmx hmx前缀加和 s s s ,从上一次 pushdown 操作后最大的前缀加和 m s ms ms

然后在操作时,最重要的是 pushup 和 pushdown ,

对于这三个简单操作,pushup 并不难,pushdown 才是重点。

在 pushdown 的时候,我们要做一个

tr[son].hmx=max(tr[son].hmx,tr[son].mx+tr[pa].ms)tr[son].ms=max(tr[son].ms,tr[son].s+tr[pa].sm)

就相当于把父节点上的操作接到了子节点上,我们可以用两个队列相接来理解。

这样就可以保证区间历史最值的更新了。

区间历史最值还可以与区间赋值等一坨东西结合起来,总之很烦。

区间第 K K K 小值

这个属实是我没想到的…

给你一个长度为 n n n 的序列,有 Q Q Q 次操作,操作类型如下:

0 u v t :对于所有 i i i , $u \leq i \leq v $ , min ⁡ ( a i , t ) → a i \min(a_i,t) \to a_i min(ai,t)ai

1 u v k :对于所有 i i i , $u \leq i \leq v $ , 输出第 k k k 小的 a i a_i ai

基础的区间第 K K K 小值查询就是在权值线段树上二分,找到那个值,这里也是一样。

在修改时,外层的线段树可以打上取 min ⁡ \min min 的标记,但这里有一个问题:它无法实现快速的标记下传与修改。

这个时候,我们可以把相同的数放到一起修改,这就体现了势能线段树的思想。

修改时,对于外层线段树上拆出的 log ⁡ 2 n \log_2^n log2n 个节点,找出这个节点上所有本质不同的大于 t t t 的数,在这个节点及其祖先上把这些数改为 t t t 即可。

我们发现把每个节点内本质不同的数的个数减一,都要在 log ⁡ 2 n \log_2{n} log2n 棵内层线段树上修改,时间复杂度直接到了 O ( log ⁡ 2 2 n ) O(\log_2^2{n}) O(log22n)

总复杂度为 $ O((n+Q)\log_2^3{n})$​ 。

详见 [学习笔记]吉司机线段树 - epic01 - 博客园 (cnblogs.com).

关于这个用途,我找不到题源(难受)。


模版题

区间最值操作

  1. Gorgeous Sequence - HDU 5306 - Virtual Judge (vjudge.net):区间赋最小值,区间和。
  2. 最假女选手 - 黑暗爆炸 4695 - Virtual Judge (vjudge.net):区间赋最小,大值,区间加,区间和,区间求最大,小值。
  3. U180387 CTSN loves segment tree - 洛谷 | 计算机科学教育新生态 (luogu.com.cn):双数组结合,区间赋最小值,区间加。
  4. Mzl loves segment tree:区间赋最小,大值,区间加。

(3,4题详见区间最值操作 & 区间历史最值 - OI Wiki (oi-wiki.org))

区间历史最值

  1. CPU 监控 - 洛谷 P4314 - Virtual Judge (vjudge.net):区间加,区间覆盖,历史最大值。
  2. Good Subsegments - CodeForces 997E - Virtual Judge (vjudge.net):历史区间最小值,历史区间最小值个数

混合题目

  1. 线段树 3(区间最值操作、区间历史最值) - 洛谷 P6242 - Virtual Judge (vjudge.net)

相关资料

  1. 线段树历史最值问题 - Flandre-Zhu - 博客园 (cnblogs.com)
  2. [学习笔记]吉司机线段树 - epic01 - 博客园 (cnblogs.com)
  3. 区间最值操作 & 区间历史最值 - OI Wiki (oi-wiki.org)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值