对于区间最值的查询和维护,用线段树也能完成.可是树状数组比较好写,时间复杂度也不高,不用白不用-。-
用树状数组求区间最值时,先得想一个问题:lowbit(x)在树状数组的作用究竟是什么.
在树状数组最基本区间求和中tree[x]代表的是tree[x-lowbit(x)+1,x]的和.比如tree[8]=arr[1]+arr[2]+…+arr[8]
由此不难发现lowbit可以表示区间的长度.这点在求区间最值的时候比较重要.
既然用来求区间最值问题,那么原来的树状数组模板也得变变,update函数由原来的区间和更新变成区间最值更新,所以此时tree[x]表示的是tree[x-lowbit(x)+1,x]的最大值.
void update(int x,int val)
{
while(x<=n)
{
tree[x]=MAX(tree[x],val);
x+=lowbit(x);
}
}
现在tree中保存的都是对应区间的最大值了.那怎么求[L,R]范围的最大值呢.如果从L一路莽到R的话,是可以求出最大值,不过求出的最大值可能是[1,R]之间的最大值,并不是所需要的[L,R]之间的最大值.别忘了,tree在更新时是由下至上,且根据lowbit划分了每个区间(例如:tree[3]=arr[3],tree[4]=arr[1]~arr[4]).所以问题又来了,怎么避免[1,L-1]呢.既然如此不如从右往左开始查询,len表示[L,R]的长度,len=R-L+1.前面提到过tree[x]=[x-lowbit(x)+1,x]之间的最大值,lowbit(x)可以表示区间长度,所以如果len>= lowbit(R),那么tree[R]可以直接取出来比较ans=max(ans,tree[R]).例如求[4,7]之间的最大值,len=4,lowbit(7)=1(tree[7]=arr[7]).那如果lowbit(R)>len呢,这个好办,直接拿arr[R]来比较就行了,ans=max(ans,arr[R]).
HDU1754经典的区间最值问题
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define lowbit(x) ((x)&(-x))
#define MAX(a,b) (((a)>(b))?(a):(b))
using namespace std;
int tree[200010],arr[200010],n;
void update(int x,int val)
{
while(x<=n)
{
tree[x]=MAX(tree[x],val);
x+=lowbit(x);
}
}
int query(int L,int R)
{
int len=R-L+1,ret=0;
while(len&&R)
{
if(len>=lowbit(R))
{
ret=MAX(tree[R],ret);
len-=lowbit(R);
R-=lowbit(R);
}
else
{
ret=MAX(arr[R],ret);
len--,R--;
}
}
return ret;
}
int main()
{
int m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
update(i,arr[i]);
}
while(m--)
{
char s[2];
int a,b;
scanf("%s%d%d",s,&a,&b);
if(s[0]=='U')
{
arr[a]=b;
update(a,b);
}
else
{
int ans=query(a,b);
printf("%d\n",ans);
}
}
}
return 0;
}
如有错误烦请指出,感激不尽