题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754
解法一:树状数组:
首先要注意下query()函数里的操作和update()函数里的操作。
先解释下update()函数里的操作,令原数组a[i] = num,即每次都使得a[i]中保留第i个学生最新的成绩。这个操作在query()函数里将会有较大的用处。
再来说下关于query()的操作,首先当l == r的时候,当前区间的最大值肯定就是a[l];当l != r时,我们要求[l,r]区间里的最大值,又可以知道,tree[]数组中保留的是前缀值,所以单纯的按照以前的思路用r - (l-1)求区间最大值肯定不行。我们都了解树状数组的存储方式,当i=4时,tree[i]存储的是前4个的前缀值,当i=6时,tree[i]中存储的是5和6之间的最大值,假设题目让求区间[3,8]的最大值,肯定不能比较tree[8],因为tree[8]中存储的是 前8个数的最大值,该值可能不在当前要查询的区间内。退而求其次,我们可以比较a[8]的值,因为a[8]中存储的是第8个人的最大值。但是我们可以直接求tree[6],因为tree[6]中存储的是第5个和第6个的最大值。但是当我们遇到r == 8的时候该怎么办呢? 假设当r==8时,则 r - (r & -r) = 0,肯定小于左区间l的值,也就是说如果当前 i - (i & -i) >= l,则该点的tree[]值所管的点都在该[l,r]区间内,否则 比较a[i],然后 i--即可。
AC代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <bitset>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <cassert>
#include <ctime>
#define rep(i,m,n) for(i=m;i<=(int)n;i++)
#define mod 100000007
#define inf 0x3f3f3f3f
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define pi acos(-1.0)
#define pii pair<int,int>
#define sys system("pause")
#define ls rt<<1
#define rs rt<<1|1
#define all(x) x.begin(),x.end()
const int maxn=2e5+10;
const int N=5e2+10;
using namespace std;
ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);}
ll qmul(ll p,ll q,ll mo){ll f=0;while(q){if(q&1)f=(f+p)%mo;p=(p+p)%mo;q>>=1;}return f;}
ll qpow(ll p,ll q,ll mo){ll f=1;while(q){if(q&1)f=qmul(f,p,mo)%mo;p=qmul(p,p,mo)%mo;q>>=1;}return f;}
int n,m,tree[maxn],a[maxn];
int lowbit(int k){
return k & -k;
}
void update(int k, int num){
a[k] = num;
while(k <= n){
tree[k] = max(tree[k],num);
k += lowbit(k);
}
}
int query(int l, int r){
int ans = a[r];
while(l != r){
for(r -= 1; r - lowbit(r) >= l; r -= lowbit(r)){
ans = max(ans,tree[r]);
}
ans = max(ans,a[r]);
}
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
memset(tree,0,sizeof(tree));
for(int i = 1; i <= n; i ++){
scanf("%d",&a[i]);
update(i,a[i]);
}
char str[10];
while(m --){
scanf("%s",str);
int i,j;
scanf("%d%d",&i,&j);
if(str[0] == 'Q'){
int ans = query(i,j);
printf("%d\n",ans);
}
else{
update(i,j);
}
}
}
return 0;
}
解法二:线段树
在一个总的区间的最大值mxx = max(左区间最大值,右区间最大值);
AC代码如下:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define inf 0x3f3f3f3f
#define ll long long
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
using namespace std;
const int maxn = 2e5 + 5;
int n,m,sum[maxn<<2],add[maxn<<2],a[maxn];
void PushUp(int rt){
sum[rt] = max(sum[ls],sum[rs]);
}
void Build(int l, int r, int rt){
if(l == r){
sum[rt] = a[l]; return;
}
int m = (l + r) >> 1;
Build(l,m,ls); Build(m+1,r,rs);
PushUp(rt);
}
void Update(int L, int C, int l, int r, int rt){
if(l == r){
sum[rt] = C; return ;
}
int m = (l + r) >> 1;
if(L <= m) Update(L,C,l,m,ls);
else Update(L,C,m+1,r,rs);
PushUp(rt);
}
int Query(int L, int R, int l, int r, int rt){
if(L <= l && r <= R) return sum[rt];
int m = (l + r) >> 1;
int ans = 0;
if(L <= m) ans = max(ans,Query(L,R,l,m,ls));
if(R > m) ans = max(ans,Query(L,R,m+1,r,rs));
return ans;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i = 1; i <= n; i ++)
scanf("%d",&a[i]);
memset(sum,0,sizeof(sum));
memset(add,0,sizeof(add));
Build(1,n,1);
char str[10];
while(m --){
scanf("%s",str);
int b,c;
if(str[0] == 'Q'){
scanf("%d%d",&b,&c);
int ans = Query(b,c,1,n,1);
printf("%d\n",ans);
}
else{
scanf("%d%d",&b,&c);
Update(b,c,1,n,1);
}
}
}
return 0;
}