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

120 lines
4.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

##[$I$ $Hate$ $It$](http://acm.hdu.edu.cn/showproblem.php?pid=1754)
### 一、题目描述
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202308151526134.png)
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202308151527276.png)
### 二、数组的含义
本题是 **单点修改**,**区间求极大极小值** 的 **模板题**
* 在维护和查询区间和的算法中,$tr[x]$中储存的是$[x-lowbit(x)+1,x]$中每个数的和
* 在求区间 **极值** 的算法中,$tr[x]$储存的是$[x-lowbit(x)+1,x]$中所有数的 **极值**
* 求区间极值的算法中还有一个$a[i]$数组,表示第$i$个数是多少
### 三、单点修改引发的变化
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202308151631637.png)
#### 数学原理
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202308151632006.png)
#### 查询最值
   设 $query(x,y)$ 求区间 $[x,y]$ 之间的最值, 已知 $c[x]$ 表示 $[x−lowbit(x)+1,x]$ 之间的最值,那如何求区间 $[x,y]$ 的最值呢?
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/{year}/{month}/{md5}.{extName}/202308151652919.png)
我们不难发现:
如果求区间 $[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]$
所以,我们发现下面的规律,因为 $y−lowbit(y)+1$ 表示 $c[y]$ 结点所管辖范围的最左边的点
- ① 若 $y−lowbit(y)+1>=x$, 则$query(x,y)=max(c[y],query(x,y−lowbit(y)))$;
- ② 若 $y−lowbit(y)+1<\ \ \ x$, 则 $query(x,y)=max(a[y],query(x,y−1))$;
- 边界 $x>y$
```cpp {.line-numbers}
// 对于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$
```cpp {.line-numbers}
#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;
}
```