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.

132 lines
4.1 KiB

2 years ago
##[$B$ - $I$ $Hate$ $It$ $HDU$ - $1754$](http://acm.hdu.edu.cn/showproblem.php?pid=1754)
### 一、题目描述
很多学校流行一种比较的习惯。老师们很喜欢询问,从 **某某** 到 **某某** 当中,分数最高的是多少。这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要 **更新某位同学** 的成绩。
**$Input$**
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 $N$ 和 $M$ ( $0<N<=200000,0<M<5000$ )
学生$ID$编号分别从$1$编到$N$。
第二行包含$N$个整数,代表这$N$个学生的初始成绩,其中第$i$个数代表$ID$为$i$的学生的成绩。
接下来有$M$行。每一行有一个字符 $C$ (只取'$Q$'或'$U$') ,和两个正整数$AB$。
当$C$为'$Q$'的时候,表示这是一条询问操作,它询问$ID$从$A$到$B$(包括$A,B$)的学生当中,成绩最高的是多少。
当$C$为'$U$'的时候,表示这是一条更新操作,要求把$ID$为$A$的学生的成绩更改为$B$。
**Output**
对于每一次询问操作,在一行里面输出最高成绩。
**Sample Input**
```cpp {.line-numbers}
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**
```cpp {.line-numbers}
5
6
5
9
```
### 二、解题思路
- 单点修改, 区间查询
```cpp {.line-numbers}
#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;
}
```