HDU 1754 I hate it 树状数组维护区间最大值

时间:2023-03-09 20:08:18
HDU 1754 I hate it 树状数组维护区间最大值

Problem Description

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

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

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 Output

5
6
5
9

分析

  今天突然想起我ST表还不会打,所以打算找一个区间最大值来做,于是就有了这道题,但我做的时候,突然好奇树状数组能不能来干这件事,想了想,魔改了一下代码,然后发现一直改不对,然后就去百度了一下,发现对于单点修改的问题,树状数组还是可以解决的,不必建立线段树。

  对于修改,如果把所有的点都重新计算一边,固然可以,但是效率太低,所以考虑树状数组的特有性质,对于一个点,只有距离在lowbit之内的点才能影响到它,比如点5,lowbit5=1,故只有自己影响自己,而4呢,lowbit4=4,所以影响它的点有1,2,3,而3=4-1,2=4-2,1还用不用考虑呢?显然是不用的,因为1是2的儿子,在修改2的时候便已经考虑过1了,所以只需一个循环套循环就能解决这个问题。

  对于查询,求区间加法的时候可以进行加减,但最大值显然不行,所以考虑别的查询方法。如果查询区间[l,r]的话,那么如果r-lowbit(r)>l,说明r的范围不包括l,所以可以将这个区间分成两部分[r-lowbit r+1,r]

和区间[l,r-lowbit r],而前者就恰好是tree[r],可以自行取值验证,如果r-lowbit(r)<l呢,说明r的范围已经包括了l,那么就将这个区间分为两部分,a[r]和[l,r-1],然后再递归查询就行,你会发现递归是没有终点的,会死循环,所以要加一个终点,终点显然就是l==r,这样的话区间就只包括a[r]了,所以直接返回a[r]的值,这样就完成了树状数组的区间最大值查询,区间最小值也是一样的道理,换一下函数名就行了,是不是很神奇呢?

  于是就掌控了一门神奇的技能,于是我还是没练习ST表,算了晚上再弄吧

  

 #include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2e5+;
int lowbit(int x){
return x&-x;
}
int tmx[N],a[N],n;
void Add(int x){
while(x<=n){
tmx[x]=a[x];
int lim=lowbit(x);
for(int i=;i<lim;i<<=){
tmx[x]=max(tmx[x],tmx[x-i]);
}
x+=lowbit(x);
}
}
int query(int l,int r){
if(l==r)return a[l];
if(r-lowbit(r)>l)return max(tmx[r],query(l,r-lowbit(r)));
else return max(a[r],query(l,r-));
}
int main(){
int m;
while(~scanf("%d%d",&n,&m)){
memset(tmx,,sizeof(tmx));
for(int i=;i<=n;i++)
scanf("%d",&a[i]),Add(i);
while(m--){
char op[];int l,r;
scanf("%s%d%d",op,&l,&r);
if(op[]=='Q')printf("%d\n",query(l,r));
else {
a[l]=r;
Add(l);
}
}
}
return ;
}