转载处: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;
}
来源:https://www.cnblogs.com/GXXX/p/6837955.html
