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
;
}