Files
python/TangDou/AcWing/SegmentTree/ZKW/HDU1754.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

4.1 KiB
Raw Blame History

##B - I Hate It HDU - $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

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;
}