蓝桥杯 算法训练 操作格子(线段树,点更新)

算法训练 操作格子  

时间限制:1.0s   内存限制:256.0MB
      
问题描述

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


 
  线段树,点更新
  线段树每一个节点代表一个区间,并存储有这个区间的特定值,例如这个区间最大值,区间和等等……而这道题的特定值有两个,权值和 和 最大权值。
  思路
  没有什么特殊的地方,按照正常的线段树的思路来做即可。
  * 每个节点存储权值和val,最大权值mx;
  1.更新的时候,递归找到底,val和mx一起修改,然后一层层的返回的时候,用Push_up(),不断更新val和mx;
  2.查询权值和的时候,递归往下找,直到找到要查询的区间为止,返回该区间节点的权值和val;
  3.查询最大权值的时候,也是递归向下找,找到查询区间之后,返回节点的最大权值mx。
  以上就为基本的步骤了,另外注意Push_up()的编写。
  写这道题的时候,线段树用的还不是很熟,第一遍写完之后,测试数据不对,又修改了很多地方才通过。所以做线段树的题,要注意好细节。总的来说,这道题是一道不错的线段树的练手题。
  代码
  1 #include <iostream>
  2 #include <stdio.h>
  3 using namespace std;
  4 #define MAXN 100010
  5 struct Node{
  6     int left,right;
  7     int val,mx; //权值和最大值
  8 };
  9 Node tree[MAXN*3+1];
 10 int Max(int a,int b)    //返回两数较大值
 11 {
 12     return a>b?a:b;
 13 }
 14 void Push_up(int d)
 15 {
 16     tree[d].val = tree[d<<1].val + tree[d<<1|1].val;
 17     tree[d].mx = Max(tree[d<<1].mx , tree[d<<1|1].mx);
 18 }
 19 void Init(int d,int l,int r)    //初始化线段树
 20 {
 21     if(l==r){
 22         tree[d].left = l;
 23         tree[d].right = r;
 24         scanf("%d",&tree[d].val);   //读取数据
 25         tree[d].mx = tree[d].val;
 26         return ;
 27     }
 28 
 29     int mid = (l+r)/2;
 30     tree[d].left = l;
 31     tree[d].right = r;
 32     Init(d<<1,l,mid);
 33     Init(d<<1|1,mid+1,r);
 34     Push_up(d);
 35 }
 36 void Update(int d,int l,int r,int x)  //1.修改一个格子的权值。[l,r]为要找的区间,在这道题里l==r。x为要修改的值
 37 {
 38     if(tree[d].left==l && tree[d].right==r){    //找到区间。
 39         tree[d].val = x;
 40         tree[d].mx = x;
 41         return ;
 42     }
 43     if(tree[d].left == tree[d].right){  //没找到区间
 44         return ;
 45     }
 46 
 47     int mid = (tree[d].left+tree[d].right)/2;
 48     if(mid>=r){ //去左区间
 49         Update(d<<1,l,r,x);
 50     }
 51     else if(mid<l){ //去右区间
 52         Update(d<<1|1,l,r,x);
 53     }
 54     else {  //mid在中间
 55         Update(d<<1,l,mid,x);
 56         Update(d<<1|1,mid+1,r,x);
 57     }
 58     Push_up(d);
 59 }
 60 int Query_sum(int d,int l,int r)    //2.求连续一段格子权值和
 61 {
 62     if(tree[d].left==l && tree[d].right==r){    //找到区间
 63         return tree[d].val;
 64     }
 65     if(tree[d].left==tree[d].right){    //没找到
 66         return 0;
 67     }
 68 
 69     int mid = (tree[d].left+tree[d].right)/2;
 70     if(mid>=r){
 71         return Query_sum(d<<1,l,r);
 72     }
 73     else if(mid<l){
 74         return Query_sum(d<<1|1,l,r);
 75     }
 76     else{
 77         return Query_sum(d<<1,l,mid) + Query_sum(d<<1|1,mid+1,r);
 78     }
 79 }
 80 int Query_max(int d,int l,int r)    //3.求连续一段格子的最大值
 81 {
 82     if(tree[d].left==l && tree[d].right==r){    //找到区间
 83         return tree[d].mx;
 84     }
 85     if(tree[d].left==tree[d].right){    //没找到
 86         return 0;
 87     }
 88 
 89     int mid = (tree[d].left+tree[d].right)/2;
 90     if(mid>=r){
 91         return Query_max(d<<1,l,r);
 92     }
 93     else if(mid<l){
 94         return Query_max(d<<1|1,l,r);
 95     }
 96     else{
 97         return Max(Query_max(d<<1,l,mid),Query_max(d<<1|1,mid+1,r));
 98     }
 99 }
100 int main()
101 {
102     int n,m,q,x,y;
103     while(scanf("%d%d",&n,&m)!=EOF){
104         Init(1,1,n);    //初始化
105         while(m--){
106             scanf("%d%d%d",&q,&x,&y);
107             switch(q){
108                 case 1:
109                     Update(1,x,x,y);
110                     break;
111                 case 2:
112                     printf("%d\n",Query_sum(1,x,y));
113                     break;
114                 case 3:
115                     printf("%d\n",Query_max(1,x,y));
116                     break;
117                 default:break;
118             }
119         }
120     }
121     return 0;
122 }

 

Freecode : www.cnblogs.com/yym2013

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值