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; }