欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

2018年3月7日训练日记

程序员文章站 2024-03-19 19:05:04
...

RMQ问题中的ST算法:(摘自某位大佬)

ST(Sparse Table)算法
定义:f[i][j]表示i到i+2^j-1这段区间的最大值。
预处理:f[i][0]=a[i]。即i到i区间的最大值就是a[i]。
状态转移:将f[i][j]平均分成两段,一段为f[i][j-1],另一段为f[i+2^(j-1)][j-1]。
两段的长度均为2^j-1。f[i][j]的最大值即这两段的最大值中的最大值。

得到f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。

void RMQ(int N){
    /*注意外部循环从j开始,
    因为初始状态为f[i][0],
    以i为外层会有一些状态遍历不到。*/ 
    for(int j=1;j<=20;j++)  
        for(int i=1;i<=N;i++)
            if(i+(1<<j)-1<=N)
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
 查询:需要查询的区间为[i,j],则需要找到两个覆盖这个闭区间的最小幂区间。这两个区间可以重复,因为两个区间是否相交对区间最值没有影响。(如下图)
2018年3月7日训练日记

i+2^k-1>=j-2^k+1 自己画了下,还是很容易验证的。所以一定能包含i到j整个区间。

当然求区间和就肯定不行了。这也就是RMQ的限制性。
因为区间的长度为j-i+1,所以可以取k=log2(j-i+1)。

RMQ(A,i,j)=max(f[i][k],f[j-2^k+1][k])

若在一棵树上进行路径的权值修改,询问路径权值和、路径权值极值树上倍增树链剖分都能解决后两种问题,而且树上倍增要比树链剖分代码好打,但是树链剖分能处理权值修改问题(第一种问题),而树上倍增不能。树剖的用处比树上倍增多得多,树上倍增能做的题树剖一定能做,反过来就不行了。

树剖我记得之前看过一点,但是去翻了自己的博客居然没找到,只有自己当时打的模板。。。以下树剖内容来自(路人黑的纸巾

轻儿子和重儿子
设xsize表示以x为根的子树的节点个数 
对于每个非叶子节点y,y的儿子所有中size最大的儿子就是重儿子(两个或以上都最大的话,任意选一个) 
而y其它的儿子,都是轻儿子 (轻儿子通常没有用,我们可以不去考虑它)
重边、轻边和重链
重儿子与其父亲之间的路径,就是重边 
不是重边的边均为轻边 
多条重边相连为一条重链

相关性质:

  • 若x是轻儿子,则有xsize<=father(x)size2

  • 从根到某一点的路径上,不超过log2n重链,不超过log2n轻链

预处理

首先用一个dfs遍历,求出每个点的depthfathersize以及重儿子heavy_son

其次再用一个dfs剖分,第二个dfs维护什么呢?

  • 优先走重边的原则,遍历出来一个dfs序
  • xtop表示x位于的重链上的顶端节点编号
  • to_tree[x]表示x在线段树中的位置
  • 对于线段树中的第y个位置,to_num[y]为它对应的dfs序中的位置

2018年3月7日训练日记

剖分完以后,每一条重链相当于一个区间,我们维护处理完的数据结构即可

我们把上面那棵树剖分完以后线段树中的点顺序是这样的: 
1,3,6,10,8,9,7,2,5,4 (加粗的点是在重链上的点) 
在同一条重链上的点,在线段树的顺序中要连在一起(否则怎么维护呢)

剖树的时间复杂度 O(n)O(n)

维护以及在线操作

因为已经搞过to_tree了,通过to_tree的标号在线段树中用单点修改即可 
时间复杂度是线段树修改的O(log2n)O(log2n)

询问xxyy路径的极值(或者路径和等等),和求LCALCA一样边向上跳边记录答案 
这里记录的答案通过查询数据结构得到 

用最普通的数据结构线段树来维护树链剖分的时间复杂度是O(n+qlog2n)


hiho600题 go!

相关标签: ACM RMQ