您的位置:首页 > 其它

HDU 1754 线段树

2016-05-17 16:30 267 查看

线段树的查询区间最值、单点更新

题目说明

在每个测试的第一行,有两个正整数 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。

简单来说就是给定一个正整数数组,然后进行2个操作,一个是查询数组[l,r]区间的最大值,一个是将数组中某个位置的值替换掉。

思路

初步想法,更新的时候就直接用数组下标定位更新,200000大小的数组还是可以开辟的,查询最值的时候就遍历一下数组区间维护一个max字段就可以了,这样的时间复杂度为O(N),然后有M个操作,最坏的情况是M个操作都是查询区间,那么复杂度就有O(N*M)=200000*5000,一般10的九次为1秒,绝对超时的。

从时间复杂度入手,感觉M个操作没法优化,只能从遍历数组里优化,从O(N)优化到O(logN)一般是用树。这题应该用树状数组和线段树都可以。

代码分析

首先定义个数组tree[N*4],用于存整棵树,理论来说空间开到2N+1就可以了(数学等比可以推算),但是递归的时候需要访问叶节点的下一层才能知道递归结束(猜测),所以树的深度需要多一层。

线段树的构造(build):

代码块

#include <iostream>
#include <algorithm>
#include<cmath>
using namespace std;
int tree[200000*4];
void merge(int root){
tree[root] = max(tree[root<<1],tree[(root<<1)+1]);
}
void build(int root,int l,int r){
if (l == r){
scanf("%d",&tree[root]);
return;
}
int mid = (l+r)>>1;
build(root<<1,l,mid);
build((root<<1)+1,mid+1,r);
merge(root);
}
int getMax(int a,int b,int root,int l,int r){
if (a<=l&&r<=b){
return tree[root];
}
int mid = (l+r)>>1;
int ret = 0;
if (a<=mid){
ret = max(ret,getMax(a,b,root<<1,l,mid));
}
if (b>=mid+1){
ret = max(ret,getMax(a,b,(root<<1)+1,mid+1,r));
}
return ret;
}
void update(int a,int b,int root,int l,int r){
if (l==r){
tree[root] = b;
return;
}
int mid = (l+r)>>1;
if (a<=mid){
update(a,b,root<<1,l,mid);
}else{
update(a,b,(root<<1)+1,mid+1,r);
}
merge(root);
}
int main() {
int n,m,a,b;
char c;
while (~scanf("%d %d",&n,&m)){
build(1,1,n);
for (int i = 0; i < m; ++i) {
scanf("%*c%c%d %d",&c,&a,&b);
if (c=='Q'){
printf("%d\n",getMax(a,b,1,1,n));
} else{
update(a,b,1,1,n);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线段树