线段树初级学习

由于我是个菜鸡,所以最近才开始学习线段树。
做题遇到的,线段树的简单应用,这是我整理的简单模板。

先看题目吧:

很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,
分数最高的是多少。这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,
模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

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]中所有值的和等)。这里的函数用递归的方式定义比较好理解。

猜你喜欢

转载自blog.csdn.net/weixin_45382645/article/details/109643360