POJ 3264: Balanced Lineup

题目在此

解题思路:查询区间最大值/最小值之差,最基础的线段树应用。

代码:

#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;
}

你可能感兴趣的:(算法、区间查询、线段树)