|
|
##[$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$') ,和两个正整数$A,B$。
|
|
|
|
|
|
当$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;
|
|
|
}
|
|
|
```
|