转载处:http://blog.csdn.net/metalseed/article/details/8039326
一:线段树基本概念
1:概述
线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)!
性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍
2:基本操作(查询区间最小值)
线段树的主要操作有:
(1):线段树的构造 void build(int node, int begin, int end);
主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后回溯的时候给当前节点赋值

void build(int node, int begin, int end) { if (begin == end) segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */ else { /* 递归构造左右子树 */ build(2*node, begin, (begin+end)/2); build(2*node+1, (begin+end)/2+1, end); /* 回溯时得到当前node节点的线段信息 */ if (segTree[2 * node] <= segTree[2 * node + 1]) segTree[node] = segTree[2 * node]; else segTree[node] = segTree[2 * node + 1]; } }
(2):区间查询int query(int node, int begin, int end, int left, int right);
(其中node为当前查询节点,begin,end为当前节点存储的区间,left,right为此次query所要查询的区间)
主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息

int query(int node, int begin, int end, int left, int right) { int p1, p2; /* 查询区间和要求的区间没有交集 */ if (left > end || right < begin) return -1; if (begin >= left && end <= right) return segTree[node]; p1 = query(2 * node, begin, (begin + end) / 2, left, right); p2 = query(2 * node + 1, (begin + end) / 2 + 1, end, left, right); if (p1 == -1) return p2; if (p2 == -1) return p1; if (p1 <= p2) return p1; return p2; }
(3):区间或节点的更新及线段树的动态维护update (这是线段树核心价值所在,节点中的标记域可以解决N多种问题)
动态维护需要用到标记域,延迟标记等。
a:单节点更新

void Updata(int node, int begin, int end, int ind, int add)/*单节点更新*/ { if( begin == end ) { segTree[node] += add; return ; } int m = ( left + right ) >> 1; if(ind <= m) Updata(node * 2,left, m, ind, add); else Updata(node * 2 + 1, m + 1, right, ind, add); /*回溯更新父节点*/ segTree[node] = min(segTree[node * 2], segTree[node * 2 + 1]); }
N - I Hate It
这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
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
题解:直接套线段树的三个模板即可;
代码:

#include<iostream> #include<cstdio> using namespace std; const int mm=200005; int segTree[mm*4 + 10]; int a[mm + 10]; void build(int node,int istart,int iend) { if(istart==iend) segTree[node]=a[istart]; else { int mid=(istart+iend)/2; build(node*2,istart,mid); build(node*2+1,mid+1,iend); if(segTree[node*2]>=segTree[node*2+1]) segTree[node]=segTree[node*2]; else segTree[node]=segTree[node*2+1]; } } int query(int node,int istart,int iend,int left,int right) { int p1,p2; if(left>iend || right<istart) return -1; if(istart>=left&&iend<=right) return segTree[node]; p1=query(node*2,istart,(istart+iend)/2,left,right); p2=query(node*2+1,(istart+iend)/2+1,iend,left,right); return max(p1,p2); } void update(int node,int istart,int iend,int ind,int add) { if(istart==iend) { segTree[node]=add; return ; } int m=(istart+iend)>>1; if(ind<=m) update(node*2,istart,m,ind,add); else update(node*2+1,m+1,iend,ind,add); segTree[node]=max(segTree[node*2],segTree[node*2+1]); } int main() { int n,k,i,x,b,ans; char s; while(scanf("%d %d",&n,&k)!=EOF){ for(i=1;i<=n;i++) scanf("%d",&a[i]); build(1,1,n); while(k--){ cin>>s; scanf("%d %d",&x,&b); if(s=='Q') { ans=query(1,1,n,x,b); cout<<ans<<endl; } else { update(1,1,n,x,b); } } } return 0; }