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 | 6 3 |
输出 #1
1 | 6 |
经典RMQ(区间最值查询问题),可以用线段树和树状数组实现,这里用ST表。在实现ST表前,需要先了解几个基本概念。
1 | 1.2^log(x)>x/2 |
好的,现在开始——-
题目大意:给一个乱序数组,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++) { |
那么,我们该如何求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++) |
注意点:
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++) { |
附上完整代码:
1 |
|