该RMQ问题可以扩展如下所示:
给定的是一个n整数数组A。
n
A
query(x,y): 给出两个整数1≤ x,y≤ n,找到的最小值A[x], A[x+1], ... A[y];
x
y
A[x], A[x+1], ... A[y]
更新(X,V): 给定的整数v和1≤ x≤ n做A[x] = v。
v
A[x] = v
O(log n)使用段树在两种操作中都可以解决此问题。
O(log n)
这在纸上是一种有效的解决方案,但是在实践中,段树会涉及很多开销,尤其是如果以递归方式实现的话。
我知道一个事实,那就是解决这个问题的方式O(log^2 n)为一个(或两个,我不知道)的操作,使用二进制索引树(更多的资源可以被发现,但这个和这个是,国际海事组织,最简洁,最详尽)。对于n这种适合内存的值的解决方案,在实践中会更快,因为BIT的开销要少得多。
O(log^2 n)
但是,我不知道如何使用BIT结构执行给定的操作。例如,我只知道如何使用它来查询间隔总和。如何使用它来找到最小值?
如果有帮助,我有别人编写的代码可以满足我的要求,但我无法理解。这是一段这样的代码:
int que( int l, int r ) { int p, q, m = 0; for( p=r-(r&-r); l<=r; r=p, p-=p&-p ) { q = ( p+1 >= l ) ? T[r] : (p=r-1) + 1; if( a[m] < a[q] ) m = q; } return m; } void upd( int x ) { int y, z; for( y = x; x <= N; x += x & -x ) if( T[x] == y ) { z = que( x-(x&-x) + 1, x-1 ); T[x] = (a[z] > a[x]) ? z : x; } else if( a[ T[x] ] < a[ y ] ) T[x] = y; }
在上面的代码中,T使用0进行初始化,a是给定的数组,N其大小(无论出于何种原因,它们都从1开始索引),并upd在每次读取值时首先被调用。upd被调用之前a[x] = v被执行。
T
a
N
upd
a[x] = v
此外,p & -p与p ^ (p & (p - 1))某些BIT源中的相同,索引从1开始,零元素初始化为无穷大。
p & -p
p ^ (p & (p - 1))
谁能解释上述工作方式或我如何用BIT解决给定的问题?
从一点点摆弄的水平来看,这就是我们所拥有的:
g用于整数数据数组的普通BIT数组a存储范围和。
g
g[k] = sum{ i = D(k) + 1 .. k } a[i]
其中,D(k)仅仅是k具有最低阶1位设置为0。下面我们就来代替
D(k)
k
T[k] = min{ i = D(k) + 1 .. k } a[i]
该查询的工作方式与普通的BIT范围总和查询完全相同,只是随着查询的进行,子范围的最小值(而不是总和)有所变化。对于中的N个项目a,N中有ceiling(log N)个上限,它确定运行时间。
此更新需要更多的工作,因为O(log N)子范围最小值(即的元素)g受更改影响,并且每个子集都需要自己进行O(log N)查询来解决。这使得更新总体为O(log ^ 2 n)。
从有点麻烦的角度来看,这是非常聪明的代码。该语句x += x & -x清除1中最低顺序的连续字符串,x然后将下一个最高顺序的零设置为1。这正是您需要“遍历”原始整数的BIT的条件x。
x += x & -x