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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##B - I Hate It HDU - 1754

一、题目描述

很多学校流行一种比较的习惯。老师们很喜欢询问,从 某某某某 当中,分数最高的是多少。这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要 更新某位同学 的成绩。

Input

本题目包含多组测试,请处理到文件结束。 在每个测试的第一行,有两个正整数 NM ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。

学生ID编号分别从1编到N

第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表IDi的学生的成绩。

接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数AB

C为'Q'的时候,表示这是一条询问操作,它询问IDAB(包括A,B)的学生当中,成绩最高的是多少。

C为'U'的时候,表示这是一条更新操作,要求把IDA的学生的成绩更改为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;
}