蓝桥杯---操作格子 (线段树)

问题描述

有n个格子,从左到右放成一排,编号为1-n。

共有m次操作,有3种操作类型:

1.修改一个格子的权值,

2.求连续一段格子权值和,

3.求连续一段格子的最大值。

对于每个2、3操作输出你所求出的结果。

输入格式

第一行2个整数n,m。

接下来一行n个整数表示n个格子的初始权值。

接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。

输出格式

有若干行,行数等于p=2或3的操作总数。

每行1个整数,对应了每个p=2或3操作的结果。

样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定

对于20%的数据n <= 100,m <= 200。

对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。




通过这道题学到了线段树,这里是线段树的优秀博客

线段树入门   ,花点时间应该可以掌握,里面说得很详细,谢谢。

另外一篇线段树博客 是用结构体描述线段树这么抽象的数据结构,配图也好,开始可能更加容易理解。

感谢他们。



#include<iostream>
using namespace std;
#define max(a,b) a>b?a:b
const int MAX =100100;
int segnode[MAX *4 +100];   	//线段树所需的区间是数组的4倍 
int father[MAX *4 +100];		//标记叶子节点的下标 
int num[MAX *4 +100];			//记录区间的和 
int array[MAX];					//保存数组
/*
*  求出左右区间的和 
*/ 
void pushNum(int node)
{
	num[node]=num[node*2]+num[node*2+1];
}
/*
*   建立区间树(从上至下,递归),并回溯建立区间最大值和区间和(从下往上,回溯) 
*/ 
void buildTree(int node,int left,int right){
	if(left == right){
		segnode[node]=array[left];
		num[node]=array[left];
		father[left]=node;				//标记下标 
	} else {
		buildTree(node*2,left,(left+right)/2);
		buildTree(node*2+1,(left+right)/2+1,right);	
		
		//回溯得到线段树的信息
		if(segnode[node*2] >= segnode[node*2+1])
			segnode[node] = segnode[node*2];
		else
			segnode[node] = segnode[node*2+1]; 
		
		pushNum(node);
	}
}
/*
*		单点更新(自下往上)
*   前面标记了叶子节点的下标,在这里发挥了作用,可以直接修改叶子节点的值,再向上更新 
*/
void updateTree(int node){
	if(node==1) return ;	//更新至顶点 
	int fa=node/2;			//父节点 
	int a=segnode[fa*2];	//左右子节点 
	int b=segnode[fa*2+1];
	segnode[fa]=max(a,b);	//更新父节点 
	pushNum(fa);			//同时更新父节点的区间和 
	updateTree(fa); 		//递归 
}
/*
*  找到区间最大值
* 	node是树节点
* 	left,right是当前节点储存的区间
*   begin,end是要查询的区间
*/ 
int query(int node,int left,int right,int begin,int end){
	if(begin>right || end<left) 		//超出范围 
		return -1; 
	if(begin <=left && right <= end) 	//当前区间被全包含	
		return segnode[node];			//返回节点值 
	int p1,p2;							//否则 ,二分当前区间,找出左右子区间的最大值 
										//由于最大区间树已经建好,所有被包含的区间都是区间的最大值 
	p1=query(node*2,left,(left+right)/2,begin,end);		
	p2=query(node*2+1,(left+right)/2+1,right,begin,end);
	
	if(p1==-1) return p2;
	if(p2==-1) return p1;
	if(p1 >= p2 ) return p1;			//比较左右区间最大值 
	else return p2;		
 }
/*
*    找到区间和
*    node为节点,
*    left,right 是当前区间 
*    begin,end  为查询区间 
*/ 
int queryNum(int node,int left,int right,int begin,int end)
{
	if(begin <=left && right <= end)   //当前区间被包含 
		return num[node];
	int mid=(left+right)/2;				//否则,采用二分法 
	int ans=0;
	if(begin <= mid)					//如果包含左区间部分 
		ans+=queryNum(node*2,left,mid,begin,end);
	if(end > mid )						//如果包含右区间部分 
		ans+=queryNum(node*2+1,mid+1,right,begin,end);
		
	return ans;							//返回区间和 
}
int main()
{
	//freopen("1.txt","r",stdin); // 读取文件 
	int n,m;
	int p,x,y;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>array[i];
	buildTree(1,1,n);
		while(m--){
			cin>>p>>x>>y;
			if(p==1){		
					segnode[father[x]]=y;
					num[father[x]]=y;
					updateTree(father[x]);		
			} else if(p==2){
					printf("%d\n",queryNum(1,1,n,x,y));		
			} else if(p==3){
					printf("%d\n",query(1,1,n,x,y));
			}
		}		
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值