题目大意
给出一段数字,代表1~n的学生的成绩,然后输入命令,一个是查找在[a, b]区间的学生成绩最大值,二是修改指定学生的成绩。从题意中可以看出的是操作的数量可以达到5000,所以用scanf可以防止超时(这次可算长记性了)。
思路
又是一道线段树模版题,以前做过一道区间求和问题。这道题是区间求最大值问题,换汤不换药。思路基本在代码注释中体现了。
代码
#include <cstdio>
#include <climits>
#define Max(a, b) (a > b ? a : b)
const int maxn = (2e5 + 1) * 4;
struct node {
int l, r, maxnum; /* l是区间的左边界, r是右边界, maxnum是这个区间的最大的数 */
};
node tree[maxn]; /* 用来存线段树的结点 */
int ans;
void build(int l, int r, int k) { /* 创建一棵线段树 */
tree[k].l = l;
tree[k].r = r;
if (l == r) {
scanf("%d", &tree[k].maxnum);
getchar(); /* 吃回车,因为后面要用scanf吃char,不吃的话下面的吃char的scanf就吃回车了 */
return;
}
int m = (l + r) / 2;
build(l, m, k * 2);
build(m + 1, r, k * 2 + 1);
tree[k].maxnum = Max(tree[k * 2].maxnum, tree[k * 2 + 1].maxnum);
}
void query(int l, int r, int k) { /* 用来查询l到r区间内的最大值,k是结点的编号,根结点的编号为1 */
/* 结束递归的条件就是找到这一区间,然后与将这一区间的最大值与ans的大小作比较以找到最大的值 */
if (l == tree[k].l && tree[k].r == r) {
if (tree[k].maxnum > ans) ans = tree[k].maxnum;
return;
}
int m = (tree[k].l + tree[k].r) / 2; /* 当前结点区间的中间值 */
if (r <= m) query(l, r, k * 2);
else if (l > m) query(l, r, k * 2 + 1); /* 前两个表示这个区间完全在mid的一边,直接调用子结点 */
else {
query(l, m, 2 * k); /* 找到左右区间的值最大值然后合并 */
query(m + 1, r, 2 * k + 1);
}
}
void update (int id, int num, int k) {
if (tree[k].r == tree[k].l && tree[k].r == id) {
tree[k].maxnum = num;
return;
}
int m = (tree[k].r + tree[k].l) / 2;
if (id > m) update(id, num, k * 2 + 1);
else update(id, num, k * 2);
tree[k].maxnum = Max(tree[k * 2].maxnum, tree[k * 2 + 1].maxnum); /* 合并,这一步很重要 */
}
int main() {
//freopen("input.txt", "r", stdin);
int n, m;
while(~scanf("%d%d", &n, &m)) {
build(1, n, 1);
char op;
int a, b;
while(m--) {
ans = INT_MIN;
scanf("%c%d%d", &op, &a, &b);
getchar();
if (op == 'Q') {
query(a, b, 1);
printf("%d\n", ans);
}
else if (op == 'U'){
update(a, b, 1);
}
}
}
return 0;
}