由于我是个菜鸡,所以最近才开始学习线段树。
做题遇到的,线段树的简单应用,这是我整理的简单模板。
先看题目吧:
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,
分数最高的是多少。这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,
模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
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<algorithm>
#include<string>
using namespace std;
const int maxn=200005;
int Max[maxn<<2];
int A[maxn];
void pushup(int rt){//更新节点信息,求最大值
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}
void build(int l,int r,int rt){//建立线段树
if(l==r){//区间长度为1,直接建立
Max[rt]=A[l];
return;
}
int m=(l+r)>>1;//中点
build(l,m,rt<<1);//建立左子树
build(m+1,r,rt<<1|1);//建立右子树
pushup(rt);//左右子树都有了之后就建立当前节点
}
void update(int L,int C,int l,int r,int rt){//更新节点
if(l==r){//区间长度为1,直接更新这个节点
Max[rt]=C;
return;
}
int m=(l+r)>>1;
if(L<=m) update(L,C,l,m,rt<<1);//更新左子树
else update(L,C,m+1,r,rt<<1|1);//更新右子树
pushup(rt);//子节点更新了信息,所以本节点也要更新信息
}
int query(int L,int R,int l,int r,int rt){//查询
if(L<=l&&R>=r){
return Max[rt];
}
int m=(l+r)>>1;
int ans=0;
if(L<=m) ans=max(ans,query(L,R,l,m,rt<<1));
if(R>m) ans=max(ans,query(L,R,m+1,r,rt<<1|1));
return ans;
}
int main()
{
int N,M;
while(scanf("%d%d",&N,&M)!=EOF){
for(int i=1;i<=N;i++)scanf("%d",&A[i]);//读入N个学生的数据
build(1,N,1);//建立线段树
while(M--){
string c;
int a,b;
cin>>c>>a>>b;
if(c=="Q"){
cout<<query(a,b,1,N,1)<<endl;
}
else update(a,b,1,N,1);
}
}
return 0;
}
简单介绍一下线段树,因为我也不是很熟悉:
原始的数据我存放在数组A中,这样比较好理解,当然可以将代码优化一下,数组A可以不用,直接将输入的数据存储在数组Max中。
数组Max的大小是数组A的四倍,这里不去推导了,可以去查资料,或者干脆记住就行。
代码中定义了四个函数:
1.pushup(int rt) 用来更新rt点的值
2.build(int l,int r,int rt)用来建立区间[l,r]的线段树
3.update(int L,int C,int l,int r,int rt) 代表的含义视题目而定,本题含义A[L]=C
4.query(int L,int R,int l,int r,int rt) 询问区间[L,R]的最大值
[l,r]表示区间,rt表示代表这段区间结果的节点在线段树(用数组实现)的下标。如果用数组表示一棵完全二叉树,假设根节点的下标为rt,那么它的两个孩子节点的下标为(2rt) 和 (2rt + 1)。整棵线段树的根节点下标为1,它的值表示数组A中区间[1,N]中的最大值(本题要查询最大值,视情况而定,线段树节点的值还可能表示区间[l,r]中所有值的和等)。这里的函数用递归的方式定义比较好理解。