引例
洛谷P3865
R
M
Q
RMQ
RMQ的中文翻译为:静态区间最值查询.英文我不知道所以不写给你
n
n
n个数,
m
m
m次查询,查询的内容为区间[l,r]中的最大值.
R
M
Q
RMQ
RMQ有解法蛮多的,st表,线段树,树状数组,划分树都可以做.
s
t
st
st表的复杂度为预处理
O
(
n
∗
log
2
n
)
O(n*\log_2 n)
O(n∗log2n)+查询
O
(
m
)
O(m)
O(m)
而线段树则需要预处理
O
(
n
∗
log
2
n
)
O(n*\log_2 n)
O(n∗log2n)+查询
O
(
m
∗
log
2
n
)
O(m*\log_2 n)
O(m∗log2n)
树状数组没学,不清楚
线段树可以看我之前的博客.
定义
这个算法就是基于 D P DP DP和位运算符,我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示从第 i i i 位开始,到第 i + 2 j − 1 i + 2^j -1 i+2j−1 位的最大值或者最小值。
那么我求
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]的时候可以把它分成两部分,第一部分从
i
i
i 到
i
+
2
(
j
−
1
)
−
1
i + 2 ^{(j-1)} - 1
i+2(j−1)−1 ,第二部分从
i
+
2
(
j
−
1
)
i + 2 ^{(j-1)}
i+2(j−1) 到
i
+
2
j
−
1
i + 2^j- 1
i+2j−1,那么可以得到
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
+
(
1
<
<
(
j
−
1
)
)
]
[
j
−
1
]
)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1])
dp[i][j]=max(dp[i][j−1],dp[i+(1<<(j−1))][j−1])
当
j
=
0
j=0
j=0时,求的是长度为1的区间的最小值,
j
=
1
j=1
j=1时,求的是长度为2的区间最小值
j
=
2
j=2
j=2时,求的是长度为4的区间最小值
以此类推,故可在
O
(
n
log
2
n
)
O(n\log_2 n)
O(nlog2n)的复杂度处理完.
如图所示
查询的话,只需要反过来就阔以了.
完整代码
这里mm[i] = mm[i - 1] +((i&(i - 1)) == 0);
只有在这里预处理后,才能真正做到查询
O
(
1
)
O(1)
O(1).
const int MAXN = 1e5 + 10;
int dp[MAXN][31],a[MAXN],mm[MAXN];
void initRMQ(int n)
{
mm[0] = -1;
for (int i = 1; i <= n; i++) {
mm[i] = mm[i - 1] +((i&(i - 1)) == 0);
dp[i][0] = a[i];
}
for (int j = 1; j <= mm[n]; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
}
int rmq(int x, int y)
{
int k = mm[y - x + 1];
return max(dp[x][k], dp[y - (1 << k) + 1][k]);
}
int main()
{
int n,m;
cin >> n >> m;//scanf("%d", &n);
for (int i = 1; i <= n; i++)
cin >> a[i];
initRMQ(n);
while (m--) {
int x, y;
cin >> x >> y;
cout << rmq(x, y) << '\n';
}
return 0;
}
二维st表
暂存
https://blog.csdn.net/VictoryCzt/article/details/83684082
约束RMQ
https://www.cnblogs.com/ghostcai/p/9280720.html
https://blog.csdn.net/VictoryCzt/article/details/83348579
练习题目
洛谷P2251
裸的
R
M
Q
RMQ
RMQ问题
洛谷P3865
s
t
st
st表模板题目
洛谷P2048
s
t
st
st表+前缀和+贪心+堆优化