rmq范围最小(大)指问题——处理对于n个数,要求快速求出某区间内的最值。
f[i][j]表示以i为起点长度为2^j的最值。
f[i][j] = rmq(f[i][j-1], f[i + 2 ^ (j-1)][j-1];相当于把线段分成相等的左右两段合并求rmq。但这得让线段长度是2的幂次。
查询时,例如f[1,7] = f[1][2] + f[5][1] + f[7][0];为O(logn),f[1, 7]对应于数组中的区间
为了加快查询,可使f[1,7] = f[1][2] + f[4][2];为O(1),这里用2=(2^k <= 7)k的最大值7 - 2^2 + 1 为4,即让7向左移动2^2 - 1个位,2^k = 1 << k;注意:例如给出n=7,则f[4][2]是求对应于[4, 8]的rmq,而f[4, 8] = f[4, 7],所以要对此进行预处理,让右区间的值超过n的为右区间为n时的值,这样才能加快查询,即多增加了一些方便查询的数据。最大左移是k = log2(n),可表示的最大起始点是1 << k,则f[i][j]表示的右区间的最大值为
(1 << k) + (1 << k) = 1 << (k + 1) = 2 ^ (log2(n) + 1),提供这个数据时能给出何时得空间[n + 1][log2(n) + 1 + 1]。
代码:
#include
<
stdio.h
>
#include
<
stdlib.h
>
#include
<
math.h
>
#define
maxn 50010
#define
Min(a, b) a < b ? a : b
#define
Max(a, b) a > b ? a : b
struct
t
{
int
min, max;
}map[maxn][
20
];
void
build (
int
n)
{
int
i, j, m, r
=
n, c
=
log((
double
)n)
/
log(
2.0
);
for
(i
=
1
; i
<=
c; i
++
)
{
for
(j
=
1
; j
<=
r; j
++
)
{
m
=
j
+
(
1
<<
(i
-
1
));
//
右半部分的起始值
if
(m
<=
r)
{
map[j][i].min
=
Min(map[j][i
-
1
].min ,map[m][i
-
1
].min);
map[j][i].max
=
Max(map[j][i
-
1
].max, map[m][i
-
1
].max);
}
else
//
长度超出n的,就当是j起点的最后一个终点的rmq。实现这步能使查询为O(1).
{
map[j][i].min
=
map[j][i
-
1
].min;
map[j][i].max
=
map[j][i
-
1
].max;
}
}
}
}
int
query(
int
l,
int
r)
{
int
len
=
r
-
l
+
1
;
int
k
=
log((
double
)len)
/
log(
2.0
);
int
m
=
r
-
(
1
<<
k)
+
1
;
int
min
=
Min(map[l][k].min, map[m][k].min);
int
max
=
Max(map[l][k].max, map[m][k].max);
return
max
-
min;
}
int
main()
{
int
n, m, i, l, r;
scanf(
"
%d%d
"
,
&
n,
&
m);
for
(i
=
1
; i
<=
n; i
++
)
{
scanf(
"
%d
"
,
&
map[i][
0
].min);
map[i][
0
].max
=
map[i][
0
].min;
}
build (n);
while
(m
--
)
{
scanf(
"
%d%d
"
,
&
l,
&
r);
printf(
"
%d\n
"
, query(l, r));
}
return
0
;
}