You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.1 KiB
4.1 KiB
一、题目描述
很多学校流行一种比较的习惯。老师们很喜欢询问,从 某某 到 某某 当中,分数最高的是多少。这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要 更新某位同学 的成绩。
Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N
和 M
( 0<N<=200000,0<M<5000
),分别代表学生的数目和操作的数目。
学生ID
编号分别从1
编到N
。
第二行包含N
个整数,代表这N
个学生的初始成绩,其中第i
个数代表ID
为i
的学生的成绩。
接下来有M
行。每一行有一个字符 C
(只取'Q
'或'U
') ,和两个正整数A,B
。
当C
为'Q
'的时候,表示这是一条询问操作,它询问ID
从A
到B
(包括A,B
)的学生当中,成绩最高的是多少。
当C
为'U
'的时候,表示这是一条更新操作,要求把ID
为A
的学生的成绩更改为B
。
Output
对于每一次询问操作,在一行里面输出最高成绩。
Sample Input
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
Sample Outpu
5
6
5
9
二、解题思路
- 单点修改, 区间查询
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 2e5 + 1000;
// 快读
int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
// zkw线段树
int d[N << 2];
int n; // n个叶子节点
int q; // q个操作,区间查询或单点修改
int m; // m是指所有父节点们的个数
//构建重口味树
void built() {
memset(d, 0, sizeof d);
for (m = 1; m <= n + 1;) m <<= 1; // 强行开够(大于n)方便二进制访问叶节点
for (int i = m + 1; i <= m + n; i++) d[i] = read(); // n个同学的初始成绩,叶子节点
/*Q:理论上从m开始都是叶节点那为什么不从m开始而是m+1呢?
A:因为在区间查询时最大是[1,n]而这个线段树区间查询需要变成开区间,即变为(0,n+1) 所以当然要给0的位置留个空区间查询
*/
// 父亲们倒序逐个更新,是O(N)
for (int x = m - 1; x; x--) d[x] = max(d[x << 1], d[x << 1 | 1]);
}
//单点修改
void modify(int x, int c) {
//定点更新+向上统计更新,牛B啊~
//这个更新与上面的父亲们逐个更新相比,是单条路径的、爬树的更新过程,O(logN)
for (d[x = x + m] = c, x >>= 1; x; x >>= 1) d[x] = max(d[x << 1], d[x << 1 | 1]);
}
//区间查询
int query(int l, int r) {
int ans = -1;
// 闭区间改为开区间:l=l+m-1 : 将查询区间改为l-1, r=r+m+1 : 将查询区间改为r+1
// l^r^1 : 相当于判断l与r是否是兄弟节点
for (l = l + m - 1, r = r + m + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ans = max(ans, d[l ^ 1]); // l % 2 == 0 左端点 是左儿子 max(右子树),想像一下题解中0号端点(哨兵),肯定是%2=0
if (r & 1) ans = max(ans, d[r ^ 1]); // r % 2 == 1 右端点 是右儿子 max(左子树),想像一下题解中1号端点,肯定是%2=1
}
return ans;
}
int main() {
//文件输入输出
#ifndef ONLINE_JUDGE
freopen("HDU1754.in", "r", stdin);
#endif
char op[2];
while (~scanf("%d%d", &n, &q)) {
//构建zkw树
built();
while (q--) {
scanf("%s", op);
int l = read(), r = read();
if (op[0] == 'Q')
printf("%d\n", query(l, r));
else
modify(l, r);
}
}
return 0;
}