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.4 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden 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.

##I Hate It

一、题目描述

二、数组的含义

本题是 单点修改区间求极大极小值模板题

  • 在维护和查询区间和的算法中,tr[x]中储存的是[x-lowbit(x)+1,x]中每个数的和

  • 在求区间 极值 的算法中,tr[x]储存的是[x-lowbit(x)+1,x]中所有数的 极值

  • 求区间极值的算法中还有一个a[i]数组,表示第i个数是多少

三、单点修改引发的变化

数学原理

查询最值

query(x,y) 求区间 [x,y] 之间的最值, 已知 c[x] 表示 [xlowbit(x)+1,x] 之间的最值,那如何求区间 [x,y] 的最值呢?

我们不难发现:

如果求区间 [1,8] 的最值,就需要点 c[8] 如果求区间 [1,7] 的最值,就需要点 c[7],c[6],c[4] 如果求区间 [2,7] 的最值,就需要点 c[7],c[6],a[4],c[3],a[2] 如果求区间 [2,2] 的最值,就需要点 a[2] 如果求区间 [2,8] 的最值,就需要点 a[8],c[7],c[6],a[4],c[3],a[2]

所以,我们发现下面的规律,因为 ylowbit(y)+1 表示 c[y] 结点所管辖范围的最左边的点

  • ① 若 ylowbit(y)+1>=x, 则query(x,y)=max(c[y],query(x,ylowbit(y)));
  • ② 若 ylowbit(y)+1<\ \ \ x, 则 query(x,y)=max(a[y],query(x,y1));
  • 边界 x>y
// 对于y来讲它所管辖的有lowbit(y)个区间。所以对于[x,y]如果y-x>=lowbit(y)
//  那么tr[y]可以直接拿来用。而如果lowbit(y)超出了[x,y]那么就y--对x进行逼近
int query(int x, int y) {
    int mx = 0;
    while (x <= y) {
        mx = max(mx, a[y]);
        for (--y; y - x >= lowbit(y); y -= lowbit(y)) mx = max(mx, tr[y]);
    }
    return mx;
}

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 200010;

int n, m;     // n个数m个操作
int a[N];     // 原始数据
char op[110]; // 指令字符串

// 树状数组求最大值模板
int tr[N];
int lowbit(int x) {
    return x & -x;
}
// x这个位置获得了一个新值c,需要更一下c和它脑袋顶上那些统计数组的信息也就是最大值或最小值
void update(int x, int c) {
    while (x < N) tr[x] = max(tr[x], c), x += lowbit(x);
}

int query(int x, int y) { // 求x~y之间的最大值
    int mx = 0;
    while (x <= y) {
        mx = max(mx, a[y]);
        // 检查是不是可以完整覆盖掉这个区域如果是的话可以PK一下整个完整区域的极值
        // 否则,就逐步缩小范围继续取小区域中的极值
        for (--y; y - x >= lowbit(y); y -= lowbit(y)) mx = max(mx, tr[y]);
    }
    return mx;
}

/*
答案:
5
6
5
9
*/
int main() {
#ifndef ONLINE_JUDGE
    freopen("HDU1754.in", "r", stdin);
#endif
    // n个数m个操作
    while (~scanf("%d %d", &n, &m)) {
        memset(tr, 0, sizeof tr);      // 清空树状数组
        for (int i = 1; i <= n; i++) { // 读入n个数
            scanf("%d", &a[i]);
            update(i, a[i]); // i这个位置最大值是a[i],这里不是add,而是update
        }

        int x, y;
        while (m--) {
            scanf("%s %d %d", op, &x, &y);
            if (op[0] == 'U') { // 更新操作要求把id为x的学生的成绩更改为y
                a[x] = y;       // ①将原数组修改
                update(x, y);   // ②将映射的树状数组修改,使得统计信息也相应修改完成
            } else
                printf("%d\n", query(x, y)); // 询问id从x到y(包括x,y)的学生当中,最大值是多少
        }
    }
    return 0;
}