Loading

P5648 Mivik的神力 (卡常倍增,树状结构维护)

P5648 Mivik的神力

upd 2024.11.13

卡常倍增 或 树状结构维护

统计类问题,一般思路是找到数据结构维护关键信息。

这题拆不了式子,但是如果可以注意到前缀最大值的单调性,这时候就有非常多的数据结构可以维护了。

单调性怎么用?

  1. 贡献是一段段区间的形式,暴力的倍增是首想。

  2. 如果将每个位置与比它大的第一个位置相连,是树形结构。这时候询问区间会变为询问树上链和,也可以做。

想了好久也没思路。

首先很容易发现维护静态区间 max 是很容易的(st表),但是仍然不能快速维护前缀max和。

考虑前缀max 的性质。首先前缀max 的值是单调递增的,对于前缀max 序列,可以将其分成若干个内部相等的段。也就是对于每个数 \(a_i\),都有一个向右的固定的管辖范围,即到第一个比它大的数(单调栈维护)。

如果考虑优美一点的暴力,我们可以按每一段处理(每一段权值固定),最优复杂度当然是只有一段区间是,复杂度为 \(O(1)\)。但是遇到单调递增的区间,就会被卡到 \(O(n)\)

瓶颈在于每一次我们都要跳到下一个新的段,段数太多,不能一个一个跳。那我们的思路就有了——倍增

我们考虑 \(f_{i,j}\) 表示从 \(i\) 位置跳 \(2^j\) 的答案。设 \(nxt_{i,j}\) 表示从 \(i\) 位置跳 \(2^j\) 段后的位置,那么有:

\(nxt_{i,j}=nxt_{nxt_{i,j-1},j-1}\)

\(f_{i,j}=f_{i,j-1}+f_{nxt_{i,j-1},j-1}\)

第二个式子成立的原因是,\(i\) 一直跳只会有一种跳法,并且每段可以直接相加。

统计答案时从大到小枚举 \(j\),能加就加即可。这种倍增做法的复杂度显然是 \(O(n\log n+m\log n)\),可能需要一些卡常才能过。

考虑更优的做法,我们发现对于每个 \(i\),从 \(i\) 一直跳只会有一种跳法,最后一跳相当于跳到 \(n+1\)。我们发现这样构成了一个树状结构,有 \(n\) 条链,根节点是 \(n+1\)。对于每个询问区间 \([l,r]\)\(\rm maxn\) 的位置为 \(p\)。那么 \(l\)\(p\) 就对应树上的一条链,并且 \(p\)\(\rm lca\)于是 \([l,p]\) 的求解就变成了树上链求和

转化完后,维护每个节点到根节点的前缀和 \(sum_{i}\)。答案即为 \(sum_i-sum_p+calc(p,r)\)\(calc(p,r)\) 即为 \(\rm maxn\) \(\times \ (r-p+1)\)

复杂度为 \(O(n\log n+m)\),稳。

总结就是优化跳跃的题目经常会和倍增相关,如果跳跃路径固定,路径会构成树状结构,达到转化。

posted @ 2024-03-23 20:43  Fire_Raku  阅读(35)  评论(0编辑  收藏  举报