本题是单点修改,区间求极大极小值的模板题。
一、数组的含义
1、在维护和查询区间和的算法中,\(t[x]\)中储存的是\([x,x-lowbit(x)+1]\)中每个数的和。
2、在求区间最值的算法中,\(t[x]\)储存的是\([x,x-lowbit(x)+1]\)中所有数的最大值。
3、求区间最值的算法中还有一个\(a[i]\)数组,表示第\(i\)个数是多少。
二、单点修改引发的变化
一个位置被修改,原数组肯定要修改,同时,作为存储统计信息的树状数组也要进行一些修改:
void update(int x, int v) {
while (x <= n) t[x] = max(t[x], v), x += lowbit(x);
}
理解一下:从\(x\)这个位置开始,把在范围内(\(x<=n\)),比它脑袋顶上覆盖的长条(\(t[x]\))都尝试更新最大值。
三、区间查询时的思路
求区间最大值 和 求区间和有很大的不同,因为树状数组求前缀和十分方便,所以通过 \(sum (R) - sum (L-1)\) 就能得到一个区间的和;那么区间最大值怎么求?
假设要求 \(L\) 到 \(R\) 的区间最大值,我们分块仍然是根据\(lowbit(x)\) 的方式分,对应分块的最大值也很容易想到,就是求前缀和中累加的部分改成 \(max ()\) 就行了;
查询: 如果我们根据\(lowbit\) 的规则从右向左求区间的最大值,所求的结果是 \(1\) 到 \(R\) 这个区间的最大值,显然答案可能是不正确的; 我们如何避开 \(1\) 到 \(L-1\) 这个区间?
我们根据\(lowbit\)的规则,从右向左遍历区间块,如果是区间是我们要考虑的范围内就取这个最大值,如果发现当前的区间超出我们要考虑的范围就只取当前区间最末尾的值,然后\(lowbit - 1\),然后继续从右向左遍历,直到所有区间取完为止。
根据上图的步骤,下标为\(12\)的这个位置是 \(9\) 到 \(12\)的最大值,我们能通过\(lowbit\)知道当前区间的长度是多少,由此能知道当前区间是不是在我们要考虑的范围内;当遇到下标为\(8\),区间长度大于当前我们要考虑的长度时,我们就只取最末尾的值,也就是对应数组\(a[8]\),\(lowbit - 1\) 到下标为\(7\)这个位置后继续从右向左判断,直到结束。 从图中可以很容易发现,以这种方式遍历,能把所有我们需要考虑的部分都遍历了一遍。
四、代码模板
// 待研读: 树状数组 数据结构详解与模板(可能是最详细的了)
// https://blog.csdn.net/bestsort/article/details/80796531
// 求区间最大值
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
const int N = 200010;
// n个数,m个操作
int n, m;
//原始数据 / 树状数组
int a[N], t[N];
//指令字符串
char op[2];
int lowbit(int x) {
return x & (-x);
}
void update(int x, int v) {
while (x <= n) {
t[x] = max(t[x], v);
x += lowbit(x);
}
}
int query(int x, int y) {
int m1 = a[x];
while (true) {
m1 = max(m1, a[y]);
if (x == y) break;
for (y -= 1; y - x >= lowbit(y); y -= lowbit(y))
m1 = max(m1, t[y]);
}
return m1;
}
int main() {
// n个数,m个操作
while (~scanf("%d %d", &n, &m)) {
//清空树状数组
memset(t, 0, sizeof(t));
//读入n个数
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
//构建+更新树状数组
update(i, a[i]);
}
int x, y;
while (m--) {
scanf("%s%d%d", op, &x, &y);
if (*op == 'U') { //更新操作,要求把ID为x的学生的成绩更改为y
a[x] = y;
update(x, a[x]);
} else if (*op == 'Q') //询问ID从x到y(包括x,y)的学生当中,成绩最高的是多少。
printf("%d\n", query(x, y));
}
}
}