算法训练 操作格子

问题描述

有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。

//线段树;
//申请数组; 
//1.建立线段树;
//构造线段树, 
//2.查询;
//3.最大值;
//建立两个线段树,一个储存最大值,一个储存区间和; 
#include<iostream>
#include<algorithm>
using namespace std;
#define h 100010
int  T[4*h],T_sum[4*h],a[h];
int sum=0,maxn=0; 

void Build_Tree(int root,int istart,int iend){//构造线段树; 
	if(istart==iend){
		T[root]=a[istart];
		T_sum[root]=a[iend];
		return ;
	}
	
	int mid=(istart+iend)/2;
	Build_Tree(root*2,istart,mid);
	Build_Tree(root*2+1,mid+1,iend); 
	
	T_sum[root]=T_sum[root*2]+T_sum[root*2+1];//储存区间的和; 
	T[root]=max(T[root*2],T[root*2+1]); 
	return;
}
void Updata(int root,int start,int end,int index,int addVal){//线段树节点的更新; 
	if(start==end){
		T[root]=addVal;
		T_sum[root]=addVal;
		return ;
	}
	
	int mid=(start+end)/2;
	if(index<=mid){
		Updata(root*2,start,mid,index,addVal);
	}else{
		Updata(root*2+1,mid+1,end,index,addVal);
	}
	
	T_sum[root]=T_sum[root*2]+T_sum[root*2+1];
	T[root]=max(T[root*2],T[root*2+1]);
	return ;
} 
int query_sum(int root,int start,int end,int s_start,int s_end){
	if(start==s_start&&end==s_end){
		return T_sum[root];
	}
	int mid=(start+end)/2;
	if(s_end<=mid){//左子树查询 
		return query_sum(root*2,start,mid,s_start,s_end);
	} else if(s_start>mid){//查询右子树; 
		return query_sum(root*2+1,mid+1,end,s_start,s_end); 
	} else{//左右子树都需要查询; 
		return query_sum(root*2,start,mid,s_start,mid)+query_sum(root*2+1,mid+1,end,mid+1,s_end); 
	}
}
int query_max(int root,int start,int end,int m_start,int m_end){//查询最大值; 
	if(m_start<=start&&end<=m_end){
		return T[root];
	} 
	int mid=(start+end)/2;
	int temp=0;
	if(m_start<=mid){//查询左子树; 
		temp=max(temp,query_max(root*2,start,mid,m_start,m_end)); 
	}
	if(m_end>mid){//查询右子树; 
		temp=max(temp,query_max(root*2+1,mid+1,end,m_start,m_end));
	}
	return temp;
}
int main()
{
	int N = 0,M = 0, i = 0;
	scanf("%d %d",&N,&M);
	
	for(i=1;i<=N;i++){
		scanf("%d",&a[i]);
	}
	Build_Tree(1,1,N);
	
	for(i=0;i<M;i++){
		int q=0,x=0,y=0;
		scanf("%d %d %d",&q,&x,&y);
		if(q==1){//更新节点; 
			Updata(1,1,N,x,y);
		}else if(q==2){//查询区间的和; 
			sum=query_sum(1,1,N,x,y); 
			printf("%d\n",sum);
		}else if(q==3){//查询最大值; 
			maxn=query_max(1,1,N,x,y); 
			printf("%d\n",maxn);
		}
	} 
	
	return 0;
} 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值