RMQ && 树状数组 (初学)

先复习一下今天刚学的RMQ算法知识;

RMQ算法(Range Minimum Query)

:1.算法思想

求静态范围最值问题,适合于静态连续区间查询。

A[ i ] [ j ] 的值代表的是原数组中以 i 开始的连续 (1<< j)个数的最值。

2.代码

<pre name="code" class="cpp">//2.1   预处理代码

for(int j = 1 ; j != 20 ; ++j )  // 代表区间大小
    for(int i = 1 ; i + (1<<j) - 1 <= n ; ++i ) // 所求区间为 [ i ,i+(1<<j)-1 ]
        Max[ i ] [ j ] = max(Max[ i ][ j-1 ] ,Max[ i + (1<<(j-1)) ][ j-1 ] )  ;
//max{ [ i ,i + (1<<(j-1)) -1 ] ,[ i + (1<<( j-1 )) , i + (1<<j) -1 ] }
//2.2    查询代码(所求区间为【x , y 】)

int  k = ( int )( log(y - x + 1.0) / log(2.0))  ;
return max( Max[ x ][ k ] , Max[ y - ( 1<<k ) + 1 ][ k ] )  ;
//     max{  [ x , x + (1<<k) -1 ] , [ y - (1<<k) + 1 , y ]

3.时间复杂度 nlog(n)

消耗的时间主要在预处理的时间上,第一层是 log( n ) , 第二层是最多是 n 的复杂度所以总的时间复杂度是 nlog( n ) .

树状数组(binary indexed(索引) tree)

基本操作:

预备函数

定义一个Lowbit函数,返回参数转为二进制后,最后一个1的位置所代表的数值.例如,Lowbit(34)的返回值将是2;而Lowbit(12)返回4;Lowbit(8)返回8。将34转为二进制,为0010 0010,这里的"最后一个1"指的是从2^0位往前数,见到的第一个1,也就是2^1位上的1.程序上,((Not I)+1) And I表明了最后一位1的值,仍然以34为例,Not 0010 0010的结果是 1101 1101(221),加一后为 1101 1110(222), 把 0010 0010与1101
1110作AND,得0000 0010(2)

.Lowbit的一个简便求法:(C++)

int lowbit(int x)
{
    return x&(-x);
}

这里我们可以这样理解:

x 的二进制中从低位往高位的第一个 1所代表的十进制数字

lowbit( x )  = x & (-x) ;

数字在计算机里存储的形式是二进制的补码形式,-x 的存储是把 x 的二进制除符号位外全部取反,然后加1 ,加 1 操作就实现了保留了上面提到的 x 的二进制中从低位往高位的第一个1所形成的十进制数字。(解释整数范围 (- 2^15 ~ 2^15 -1))

Q:为什么用 lowbit()   ? 看完下面就明白了。。。

A:y = lowbit( x ) 得到的数就是 A[x] 的管辖范围指从 x 开始向后 y内的数都归 A[ x ] 管辖 。(这里可以这样理解:把A[ x ] 包含的所有的数记为此A[x] 的儿子,of course A[x] 就是所有儿子的父亲了)

具体操作:

3.  插点问线(  树状图,以公司管理为例见下页 )

每个元素的值代表所管辖区间的值的和 。

3.1  修改某个元素的值

当修改某个元素的值的时候必须修改所有包含他的所有的区间(即:所有的祖先),其实是分层次修改的,先修改父亲,然后修改父亲的父亲,一直修改到数组的边界。

3.2  查询某个区间的所有元素的和

当需要计算某个区间的所有元素的和的时候,我们就可以用sum( n ) - sum(m - 1) ,这样就计算出了 m 到 n 的区间内所有元素的和。求sum( n ) 其实就是把求组成 n 的整数的区间的和(这里的整数均指2的指数级的)。

树状数组讲解图

.插线问点 (以公司发工资为例)

元素的值代表的是所管辖区间内单个元素的值(即:每个元素的取值)

4.1 修改区间值

修改某个区间的值需要向下修改(见下图)。

查询某点值

查询本身及所有祖先的值。

复杂度log( n )

计算sum[ n ] 实际上是不断消除 n 的二进制中最后一个 1 ,这样 n 的二进制中最多有log ( n ) 个 1 (修改最多修改log( n )个点),所以每次操作的复杂度都是log( n )的,因此预处理的时间为 nlog(n) ,有n个元素,每个元素所用的时间为log(n) ,所以预处理的总时间为 nlog(n) .

时间: 2024-10-05 00:54:19

RMQ && 树状数组 (初学)的相关文章

UESTC 912 树上的距离 --LCA+RMQ+树状数组

1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离) 2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的节点到根节点的距离,很明显,DFS一遍后以v为根的子树在DFS序列中是连续的一段,及转化为区间更新问题,可以用树状数组. 做法:先把求LCA解决,LCA可以转化为RMQ问题,可参见:LCA转RMQ, 即转化为LCA(T,u,v) = RMQ(B,pos[u],pos[v]),其中B为深度序列.预先DFS可以处

HDU - 6393 Traffic Network in Numazu (LCA+RMQ+树状数组)

这道题相当于将这两题结合: http://poj.org/problem?id=2763 http://codeforces.com/gym/101808/problem/K 题意:有N各点N条边的带权无向图(相当于一棵树多了一条边),两种操作:修改一条边的权值:求两点间的最短路径. 分析:将任意一条边取出来,其余n-1条边可以结合LCA解最短路.询问时,比较通过取出的边和仅通过树上的边的路径的大小,最小值就是两点的最短路径. 树状数组差分维护点到根节点的距离,根据dfs序来记录需要维护的范围.

【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更快,只有O(n^2).而这货是n^2log^2n的建树...虽然查询是log^2n...但是建树那里就tle了.. 那么说题解... 先orz下,好神.. 我怎么没想到单调队列orz 首先我们维护 行 的单调队列,更新每个点在 列 距离内的最小and最大的值 然后我们维护 列 的单调队列,更新每个点

BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )

全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries + BIT 就行了.时间复杂度O(N log N). 速度垫底了QAQ 你们都会SAM.... ---------------------------------------------------------------------- #include<cmath> #include<c

树状数组求最大值 (RMQ with Shifts)

代码: #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> using namespace std; const int Max=200010; int RMQ[Max+10]; int total[Max]; int sum[35]; int N,M,cnt; char ctr[35]; int bit(int x) {//每一个下标管辖的范围 ret

树状数组(BIT)初学

BIT(Binary Indexed Tree,BIT) 树状数组.树状数组是一类怎样的数据结构呢?我们知道,树状数组是用来解决动态连续和的查询问题而诞生的. 数据结构就是说,给你n个元素的数组a[1],a[2],a[3]....a[n](下标要从1开始)然后,支持以下两种操作: 1.Add(x,y)就是说对下标为x的a[x]加d.模板默认为update(x,y) 2.Query(L,R)就是计算a[L]+a[L+1]+...+a[R].模板默认为read(x)求出1-x的和,然后read(y)

初学树状数组

原理: 有好的博客做讲解了(见参考文章),这里暂时略过,如果以后有新的理解和体会会再来写的.(应该不会) 思想: 这里可以把树状数组的精妙之处提一下(我理解的) 首先,树状数组之所以叫树状数组,因为它像树一样,有类似树的父子节点关系,这点在更新和求和操作上体现的最为明显.而最终也只是数组,因为实现起来简单方便,如数组一样.(一开始还纳闷为什么不叫二进制索引树),英文名BIT(Binary Index Tree).这个数据结构实现的功能像线段树一样,两者有着异曲同工之妙. 其次,树状数组的神奇之处

树状数组总结

树状数组 数据结构知识点1-树状数组 树状数组的用途就是维护一个数组,重点不是这个数组,而是要维护的东西,最常用的求区间和问题,单点更新.但是某些大牛YY出很多神奇的东西,完成部分线段树能完成的功能,比如区间更新,区间求最值问题. 树状数组当然是跟树有关了,但是这个树是怎么构建的呐?这里就不得不感叹大牛们的脑洞之大了,竟然能想出来用二进制末尾零的个数多少来构建树以下图为例: 从上图能看出来每一个数的父节点就是右边比自己末尾零个数多的最近的一个,也就是x的父节点就是x+(x&(-x)),这里为什么

MooFest(POJ-1990)(树状数组)

最近学习了一下树状数组,这道题纠结了很久,终究是因为没有明白树状数组怎么用. 感觉网上许多大神都只是讲原理,对于我们这些初学的菜鸟恐怕都被吓跑了. 这里我就以实用主义说一下使用方法(其实我觉得其原理应该能对我们更有启发,也许会带来很多潜在的好处): 这里需要注意的是,bit的实现代码中的bit数组一开始必须清零,这个数组并不是用来储存元素的,而是为实现这个数据结构而存在的.  你需要存储的元素是要通过那个add函数添加的,而求和则是要通过sum函数实现的,而这个bit数组的结构并不是对于一个新手