Method of Four Russians
前置:ST表
用来解决区间最值问题(RMQ)
如果将整个数组分块,每一个块的大小是
b
b
b,则可以分出
n
b
\frac{n}{b}
bn
每一个块求一个最小值出来,对这
n
b
\frac{n}{b}
bn个最小值建一个ST表,则时间复杂度
O
(
n
b
log
2
n
b
)
⊆
O
(
n
b
log
2
n
)
O\left(\frac{n}{b}\log_2\frac{n}{b}\right)\subseteq O\left(\frac{n}{b}\log_2 n\right)
O(bnlog2bn)⊆O(bnlog2n)
如果块的大小 b ∈ Θ ( log n ) b\in \Theta\left(\log n\right) b∈Θ(logn),则时间复杂度为 O ( n ) O\left(n\right) O(n)
但是这个只是块之间的RMQ,查询区间可能是跨越几个块,也可能在块内
这时候如果每一个块建一个ST表,则时间复杂度 O ( n b b log 2 b ) ⊆ O ( n log 2 b ) O\left(\frac{n}{b}b\log_2 b\right)\subseteq O\left(n\log_2 b\right) O(bnblog2b)⊆O(nlog2b),不太行
考虑在块内使用单调栈,在每一个位置存一个二进制mask,
以求区间最大值为例,则需要维护一个单调递减栈,mask表示还在栈里的元素
假设这个块为
1
,
4
,
2
,
6
,
7
,
4
,
5
,
3
1,4,2,6,7,4,5,3
1,4,2,6,7,4,5,3
i
d
x
=
0
,
a
[
i
d
x
]
=
1
,
m
a
s
k
=
00000001
b
idx =0,a[idx] = 1 ,mask = 00000001b
idx=0,a[idx]=1,mask=00000001b
i
d
x
=
1
,
a
[
i
d
x
]
=
4
,
m
a
s
k
=
00000010
b
idx =1,a[idx] = 4 ,mask = 00000010b
idx=1,a[idx]=4,mask=00000010b
i
d
x
=
2
,
a
[
i
d
x
]
=
2
,
m
a
s
k
=
00000110
b
idx =2,a[idx] = 2 ,mask = 00000110b
idx=2,a[idx]=2,mask=00000110b
i
d
x
=
3
,
a
[
i
d
x
]
=
6
,
m
a
s
k
=
00001000
b
idx =3,a[idx] = 6 ,mask = 00001000b
idx=3,a[idx]=6,mask=00001000b
i
d
x
=
4
,
a
[
i
d
x
]
=
7
,
m
a
s
k
=
00010000
b
idx =4,a[idx] = 7 ,mask = 00010000b
idx=4,a[idx]=7,mask=00010000b
i
d
x
=
5
,
a
[
i
d
x
]
=
4
,
m
a
s
k
=
00110000
b
idx =5,a[idx] = 4 ,mask = 00110000b
idx=5,a[idx]=4,mask=00110000b
i
d
x
=
6
,
a
[
i
d
x
]
=
5
,
m
a
s
k
=
01010000
b
idx =6,a[idx] = 5 ,mask = 01010000b
idx=6,a[idx]=5,mask=01010000b
i
d
x
=
7
,
a
[
i
d
x
]
=
3
,
m
a
s
k
=
11010000
b
idx =7,a[idx] = 3 ,mask = 11010000b
idx=7,a[idx]=3,mask=11010000b
查询
[
0
,
r
]
[0,r]
[0,r]的最大值,只需要找到
m
a
s
k
[
r
]
mask[r]
mask[r]最低位的
1
1
1的索引
查询
[
l
,
r
]
[l,r]
[l,r]的最大值,只需要找到
m
a
s
k
[
r
]
>
>
p
o
s
[
l
]
mask[r] >> pos[l]
mask[r]>>pos[l]的最低位
1
1
1的索引
i
i
i,最大值为
a
[
l
+
i
]
a[l+i]
a[l+i]
这样计算,单调栈时间复杂度
O
(
b
)
O\left(b\right)
O(b),进而总的时间复杂度为
O
(
n
b
b
)
O\left(\frac{n}{b}b\right)
O(bnb)
总的预处理时间复杂度为
O
(
n
)
O(n)
O(n)
P3865
#include<cstdio>
#include<cmath>
#include<algorithm>
const int N = 1e5 + 5;
const int M = 21;
int n;
int a[N];//原数组
int block_size;//块大小
int st[N][M];//块之间的st表
int Pow[M] = {1};//2的幂
int logn[N] = {-1, 0};//log2 n
int belong[N];//元素属于哪一个块
int pos[N];//块中的索引
int pre[N];//前缀最值
int sub[N];//后缀最值
int mask[N];//二进制mask
int s[N], top;//栈
void build_st() {
int cur = 0;
int id = 1;//块索引
pos[0] = -1;
for (int i = 1; i <= n; ++i) {
st[id][0] = std::max(st[id][0], a[i]);//块内最值
belong[i] = id;
if (belong[i - 1] != belong[i]) {
pos[i] = 0;
}
else {
pos[i] = pos[i - 1] + 1;
}
if (++cur == block_size) {
cur = 0;
++id;
}
}
if (n % block_size == 0) --id;
//预处理2的幂
for (int i = 1; i < M; ++i)Pow[i] = Pow[i - 1] << 1;
//预处理log2
for (int i = 2; i <= id; ++i)logn[i] = logn[i >> 1] + 1;
//块间st表预处理
for (int j = 1; j <= logn[id]; ++j) {
for (int i = 1; i + Pow[j] - 1 <= id; ++i) {
st[i][j] = std::max(st[i][j - 1], st[i + Pow[j - 1]][j - 1]);
}
}
}
void build_sub_pre() {
for (int i = 1; i <= n; ++i) {
if (belong[i] != belong[i - 1]) {
pre[i] = a[i];
}
else {
pre[i] = std::max(pre[i - 1], a[i]);
}
}
for (int i = n; i >= 1; --i) {
if (belong[i] != belong[i + 1]) {
sub[i] = a[i];
}
else {
sub[i] = std::max(sub[i + 1], a[i]);
}
}
}
void build_block() {
for (int i = 1; i <= n; ++i) {
if (belong[i] != belong[i - 1]) {
top = 0;
}
else {
mask[i] = mask[i - 1];
}
while (top > 0 && a[s[top]] <= a[i]) {
//去掉这个位置上的1
mask[i] &= ~(1 << pos[s[top--]]);
}
s[++top] = i;
mask[i] |= (1 << pos[i]);
}
}
void init() {
block_size = log2(n) * 1.5;
build_st();
build_sub_pre();
build_block();
}
int query(int l, int r) {
int bl = belong[l], br = belong[r];
if (bl != br) {//跨块
int ans1 = 0;
if (br - bl > 1) {//块间
//bl + 1 -> br - 1
int p = logn[br - bl - 1];
ans1 = std::max(st[bl + 1][p], st[br - Pow[p]][p]);
}
//左边和右边多出来的部分
int ans2 = std::max(sub[l], pre[r]);
return std::max(ans1, ans2);
}
//块内
return a[l + __builtin_ctz(mask[r] >> pos[l])];
}
int main() {
int m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
init();
while(m--){
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(l, r));
}
return 0;
}
参考:
https://oi-wiki.org/topic/rmq/#four-russian
https://codeforces.com/blog/entry/78931
相关文章
- ORA-25247: string is not a recipient of specified message ORACLE 报错 故障修复 远程处理
- ORA-29663: Unable to find a method that matches one or more of the functions ORACLE 报错 故障修复 远程处理
- ORA-29849: error occurred in the execution of ODCIINDEXSPLITPARTITION routine ORACLE 报错 故障修复 远程处理
- ORA-30388: name of the rewrite equivalence is not specified ORACLE 报错 故障修复 远程处理
- ORA-30513: cannot create system triggers of INSTEAD OF type ORACLE 报错 故障修复 远程处理
- ORA-42018: partition cannot be redefined online because of index organization incompatibility ORACLE 报错 故障修复 远程处理
- ORA-13649: The type of execution is not specified for this advisor or task. ORACLE 报错 故障修复 远程处理
- ORA-14187: partitioning method for LOCAL index is inconsistent with that of the underlying table ORACLE 报错 故障修复 远程处理
- ORA-14266: data type or length of an index subpartitioning column may not be changed ORACLE 报错 故障修复 远程处理
- ORA-16741: the destination parameter of standby database “string” has incorrect syntax ORACLE 报错 故障修复 远程处理
- Oracle Top and Ceiling of Data Range(ceiloracle)
- Unleashing the Power of SEP Linux for Efficiency(seplinux)
- Exploring the World of Linux Zone: An Introductory Guide for Beginners(linuxzone)