解题思路:查询区间最大值/最小值之差,最基础的线段树应用。
代码:
#include <cstdio>
// 线段树为完全二叉树,因此可用 root * 2 和 root * 2 + 1
// 来求得左右孩子节点的位移
#define L(root) ((root) << 1)
#define R(root) (((root) << 1) + 1)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
const int MAXN = 50001;
int cows[MAXN];
int maxn, minn;
struct st {
// 左右区间
int left, right;
// 区间最大、最小值
int max, min;
} st[MAXN * 4];
// 建树
void build(int root, int l, int r) {
// 区间为叶子节点时,初始化各值后返回
st[root].left = l, st[root].right = r;
if (l == r) {
st[root].max = st[root].min = cows[l];
return;
}
// 否则,递归构建左右节点
int m = l + ((r - l) >> 1);
build(L(root), l, m);
build(R(root), m + 1, r);
st[root].max = MAX(st[L(root)].max, st[R(root)].max);
st[root].min = MIN(st[L(root)].min, st[R(root)].min);
}
// 查询
void query(int root, int l, int r) {
// 查询范围与节点区间范围恰好重合
if (st[root].left == l && st[root].right == r) {
maxn = MAX(maxn, st[root].max);
minn = MIN(minn, st[root].min);
return;
}
// 否则,将区间一分为二
int m = st[root].left + ((st[root].right - st[root].left) >> 1);
// 查询范围“落”在左子节点区间
if (r <= m) {
query(L(root), l, r);
// 查询范围“落”在右子节点区间
} else if (l > m) {
query(R(root), l, r);
// 左右子节点区间各有一部分被查询范围覆盖:
// 将查询范围一分为二,分别查询左右子节点
} else {
query(L(root), l, m);
query(R(root), m + 1, r);
}
}
int main() {
int N, Q;
while (scanf("%d%d", &N, &Q) != EOF) {
for (int i = 1; i <= N; ++i) {
scanf("%d", &cows[i]);
}
build(1, 1, N);
int l, r;
while (Q--) {
scanf("%d%d", &l, &r);
maxn = 0, minn = 0x7FFFFFFF;
query(1, l, r);
printf("%d\n", maxn - minn);
}
}
return 0;
}