0%

ST表例题与原理

P2880 [USACO07JAN]Balanced Lineup G

题目背景

每天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. 有一天, John 决定让一些牛们玩一场飞盘比赛. 他准备找一群在对列中为置连续的牛来进行比赛. 但是为了避免水平悬殊,牛的身高不应该相差太大. John 准备了Q (1 <= Q <= 180,000) 个可能的牛的选择和所有牛的身高 (1 <= 身高 <= 1,000,000). 他想知道每一组里面最高和最低的牛的身高差别.

输入:

第1行:N,Q

第2到N+1行:每头牛的身高

第N+2到N+Q+1行:两个整数A和B,表示从A到B的所有牛。(1<=A<=B<=N)

输出:

输出每行一个数,为最大数与最小数的差

输入输出样例

输入 #1

1
2
3
4
5
6
7
8
9
10
6 3
1
7
3
4
2
5
1 5
4 6
2 2

输出 #1

1
2
3
6
3
0

经典RMQ(区间最值查询问题),可以用线段树和树状数组实现,这里用ST表。在实现ST表前,需要先了解几个基本概念。

1
2
3
1.2^log(x)>x/2
2.2进制移位,如1<<n = 2^n
3.基本的dp思想

好的,现在开始——-
题目大意:给一个乱序数组,q个询问,问区间[l,r]上的最大值和最小值的差。

首先,我们用一个二维数组maxx[i][j],记录从第i个数开始,长度为2^j的区间中的最大值。
很明显,当j=0时,maxx[i][0]记录的是从第i个数开始,长度为1的区间中的最大值,即a[i]自身。
因此,在输入a[i]时,顺便将maxx[i][0]初始化:

1
2
3
4
for (int i = 1; i <= n; i++) {
cin >> a[i];
maxx[i][0] = a[i];
}

那么,我们该如何求maxx[i][1]的值呢?

maxx[i][1]记录的是从i开始,长度为2^1的区间的最大值,也就是i和i+1的最大值,我们可写为max(i,i+1),

肯定有人会发现,这里的i和i+1不就是maxx[i][0]和maxx[i+1][0]吗!

是的,我们也可以表示为max(maxx[i][0],maxx[i+1][0]),更进一步的,可以表示为max(maxx[i][0],maxx[i+(1<<0)][0]),

进一步地进行推导,我们可以得到:maxx[i][j] = max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1])。

解释一下:

我们要求从i开始,区间长度为2^j中的最大值,我们可以将2^j拆分为2个长度为2^(j-1)的区间,它们分别从i和i+2^(j-1)开始。

这两个区间最大值的最大值,就是最开始区间的最大值。即a~c的最大值 = max(a~b的最大值,b~c的最大值)。

1
2
3
for (int j = 1; 1<<j<=n; j++) 
for (int i= 1; i + (1 << j) - 1 <= n; i++)
maxx[i][j] = max(maxx[i][j - 1], maxx[i+ (1 << (j - 1))][j - 1]);

注意点:

1.j的范围:要保证2^j<=n

2.i的范围:因为从i开始,区间长度为2^j的区间右界为i+2^j-1,所以i+2^j-1<=n


好了,处理完成,下一步是查找[l,r]上的最大值
这里就要用到我们之前提到的公式:2^log(x)>x/2。x是区间长度,那么用在这里就是:2^log(r-l+1)>(r-l+1)/2
那么,我们便可以将这个区间分为两段
第一段是从l开始,区间长度为2^log(r-l+1),第二段从r-2^log(r-l+1)+1开始,到r结束。

1
2
3
4
5
for (int i = 1; i <= m; i++) {
cin >> l >> r;
int logg = log2(r-l+1);
cout << max(maxx[l][logg], maxx[r - (1 << logg) + 1][logg]) << endl;;
}

附上完整代码:

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
#include<bits/stdc++.h>
using namespace std;
int maxx[50011][51];
int minn[50011][51];
int a[50011];
int n, m;
int main() {
int l, r;
cin >> n>>m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
maxx[i][0] = a[i];
minn[i][0] = a[i];
}
for (int i = 1; 1<<i<=n; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
maxx[j][i] = max(maxx[j][i - 1], maxx[j + (1 << (i - 1))][i - 1]);
minn[j][i] = min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]);
}
}
for (int i = 1; i <= m; i++) {
cin >> l >> r;
int logg = log2(r-l+1);
cout << max(maxx[l][logg], maxx[r - (1 << logg) + 1][logg]) - (min(minn[l][logg], minn[r - (1 << logg) + 1][logg])) << endl;;
}
return 0;
}
------ THEEND ------

欢迎关注我的其它发布渠道