ST表

Nannan Lv5

ST table

1.思想

一般用于解决RMQ (Range Minimum/Maximum Query)问题。

倍增的思想1—>2—>4—>8—>16

我们先处理出所有区间长度为1的最小值

表示左端点为,区间长度为的区间的最小值。即:

![image-20230628191602302](C:\Users\Zhou Yanan\AppData\Roaming\Typora\typora-user-images\image-20230628191602302.png)

那么:

对于一段区间,

我们要找的最大的(因为最大的一定能使得我们找的这两个区间把整个区间覆盖。)

这样我们一定能找到两个长度为的区间,分别是这两个区间有交集是没有关系的

即:每次询问我们把它分成两部分,分别是,其中,两部分结果取就是答案。

2.局限性

  1. 易看出,只有当区间中重叠的部分对最终答案无影响时,才能使用 st 表。

​ 比如区间&,|,gcd是可以的,但像求区间*或者区间+就是不行的了

  1. 不能进行修改操作

3. 复杂度:建表,查询

4. eg.RMQ

个数,个询问。

每个询问给定两个数,求出中的最大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//读入
unsigned int A, B, C;
inline unsigned int rng61() {
A ^= A << 16;
A ^= A >> 5;
A ^= A << 1;
unsigned int t = A;
A = B;
B = C;
C ^= t ^ A;
return C;
}
int main(){
scanf("%d%d%u%u%u", &n, &q, &A, &B, &C);
for (int i = 1; i <= n; i++) {
a[i] = rng61();
}
for (int i = 1; i <= q; i++) {
unsigned int l = rng61() % n + 1, r = rng61() % n + 1;
if (l > r) swap(l, r);
}
}

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//O(nlogn)建表,O(1)查询
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 1010000;
int n,q,lg[N];
unsigned int A,B,C,a[N],ans;
//unsigned int f[N][22];
unsigned int f[22][N];//尽量让靠里的那一维连续的变,让内存访问更加连续


inline unsigned int rng61() {
A ^= A << 16;
A ^= A >> 5;
A ^= A << 1;
unsigned int t = A;
A = B;
B = C;
C ^= t ^ A;
return C;
}

int main(){
scanf("%d%d%u%u%u", &n, &q, &A, &B, &C);
for (int i = 1; i <= n; i++) {
a[i] = rng61();
f[0][i] = a[i];
}
// for(int j = 1;j<=20;j++)
// for(int i = 1;i+(1<<j)-1<=n;i++)
// f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int j = 1;j<=20;j++)
for(int i = 1;i+(1<<j)-1<=n;i++)
f[j][i] = max(f[j-1][i],f[j-1][i+(1<<(j-1))]);

lg[1] = 0;//2^0
for(int i = 2;i<=n;i++)//手写,对2求lg取下整
lg[i] = lg[i/2]+1;

for (int i = 1; i <= q; i++) {
unsigned int l = rng61() % n + 1, r = rng61() % n + 1;
if (l > r) swap(l, r);
//写法1
//int len = lg[r-l+1];
//写法2
//int len = __lg(r-l+1);
//写法3
int len = 31-__builtin_clz(r-l+1);//__builtin_clz数二进制位的前导0
//2^x<=len,2^(x+1)>len
ans ^= max(f[len][l],f[len][r-(1<<len)+1]);
}
cout<<ans<<endl;
}

![image-20230705113329648](C:\Users\Zhou Yanan\AppData\Roaming\Typora\typora-user-images\image-20230705113329648.png)

5.ST 表维护其他信息

除了RQM之外,还有其它的「可重复贡献问题」,例如「区间按位和」、「区间按位或」、「区间 GCD」,ST 表都能高效地解决。

6.板子代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct S_T {
// op 函数需要支持两个可以重叠的区间进行合并
// 例如 min、 max、 gcd、 lcm 等
ll f[22][N], lg[N];
void build(int n) {
lg[0] = -1;

for (int i = 1; i <= n; ++i) {
f[0][i] = a[i];
lg[i] = lg[i / 2] + 1;
}

for (int i = 1; i <= 20; ++i)
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
f[i][j] = __gcd(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
}
void clear(int n)
{
for (int i = 1; i <= 20; ++i)
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
f[i][j] = 0;
}
ll query(int l, int r) {
if(l <= n && r > n)return __gcd(query(l,n),query(1,r-n));
int len = lg[r - l + 1];
return __gcd(f[len][l], f[len][r - (1 << len) + 1]);
}
} ST;
  • Title: ST表
  • Author: Nannan
  • Created at : 2023-09-05 19:32:00
  • Updated at : 2024-09-30 19:52:24
  • Link: https://redefine.ohevan.com/2023/09/05/四(2)、ST表 ST table/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments