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 7 3 4 2 5 1 5 4 6 2 2
3 0
经典RMQ(区间最值查询问题),可以用线段树和树状数组实现,这里用ST表。
在实现ST表前,需要先了解几个基本概念。
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 for (int i = 1; i <= n; i++) { 2 cin >> a[i]; 3 maxx[i][0] = a[i]; 4 }
那么,我们该如何求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 for (int j = 1; 1<<j<=n; j++) 2 for (int i= 1; i + (1 << j) - 1 <= n; i++) 3 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 for (int i = 1; i <= m; i++) { 2 cin >> l >> r; 3 int logg = log2(r-l+1); 4 cout << max(maxx[l][logg], maxx[r - (1 << logg) + 1][logg]) << endl;; 5 }
附上完整代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int maxx[50011][51]; 4 int minn[50011][51]; 5 int a[50011]; 6 int n, m; 7 int main() { 8 int l, r; 9 cin >> n>>m; 10 for (int i = 1; i <= n; i++) { 11 cin >> a[i]; 12 maxx[i][0] = a[i]; 13 minn[i][0] = a[i]; 14 } 15 for (int i = 1; 1<<i<=n; i++) { 16 for (int j = 1; j + (1 << i) - 1 <= n; j++) { 17 maxx[j][i] = max(maxx[j][i - 1], maxx[j + (1 << (i - 1))][i - 1]); 18 minn[j][i] = min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]); 19 } 20 } 21 for (int i = 1; i <= m; i++) { 22 cin >> l >> r; 23 int logg = log2(r-l+1); 24 cout << max(maxx[l][logg], maxx[r - (1 << logg) + 1][logg]) - (min(minn[l][logg], minn[r - (1 << logg) + 1][logg])) << endl;; 25 } 26 return 0; 27 }

浙公网安备 33010602011771号