题目链接:

http://lx.lanqiao.cn/problem.page?gpid=T18

题目描述:

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

解题思路:

乍一看,不怎么难。然而写一波循环超时之后。

本题求和、最大值操作显然不能循环计算求取,那么就要想办法再此之前保存这些值。那么问题就来了,我们要怎样保存呢?

先介绍一下线段树:

线段树是一种二叉搜索树,与区间树相似,线段树是建立在线段的基础上,每个结点都代表了一条线段[a,b]。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N(这个我表示呵呵)
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
线段树至少支持下列操作:
Insert(t,x):将包含在区间 int 的元素 x 插入到树t中;
Delete(t,x):从线段树 t 中删除元素 x;
Search(t,x):返回一个指向树 t 中元素 x 的指针。

好,我们把其代入本题:

首先说一下建树:void build(int i,int l,int r)//引入节点标和左右标

假设输入n=8,那我们需要构造的线段即[1,8],即我们的建树操作为:build(1,1,8);  //首先从节点1开始,左极限为1,右极限为8

我们的build函数有以下几个操作:

①如果l==r,也就是说,当前引入点左右相等,那么就是一个单独的数,我们就不再需要向下递归

②如果l不等于r,我们需要创造他的2个子节点:

build(2*i,l,(l+r)/2);       //我们知道二叉树子节点的编号分别为2i,2i+1

build(2*i+1,(l+r)/2+1,r);   //我们递归构造子节点时,对于奇数线段比如说[1-3],我们有两种选择,[1-2][3]或者[1-2][2-3] 这里我们采取前者。

③递归回归时将其有用的权值返回父节点,父节点针对于子节点的权值构造自身权值

比如这道题的sum与max:

/* 回归时得到当前i节点的max信息 */    
        LineTree[i].Node_Max=fmax(LineTree[2*i].Node_Max,LineTree[2*i+1].Node_Max);
        /* 回归时得到当前i节点的sum信息 */  
LineTree[i].Node_Sum=LineTree[2*i].Node_Sum+LineTree[2*i+1].Node_Sum;

接下来我们讲更新:

①当叶子节点更新的时候,所有其父路径直至根节点走要进行一次更新操作,即时间复杂度为O(logN)

②找叶子节点也需要花费O(logN)从根节点递归查找至叶子节点

最后,我们讲查询:

①查询分为点查询,线段查询(即线段左右点是否相等)

②线段查询,该线段可能在树中完全包含,也可能是半包含。例如构造线段[1,2,3,4]

即:

[1,2,3,4]

[1,2]        [3,4]

[1]    [2]    [3]    [4]

如果我们想搜寻[1,3]线段,那么可以看到,该树是不存在这样的线段的,那么我们就需要通过[1,2]和[3]来计算[1,2,3]所返回的权值

下面贴出完整代码:

#include<stdio.h>
#define N 100010
struct LineTree
{int Node_Max;//i点线段的最大值 int Node_Sum;//i点线段的和 int Node_Lpoint;//i点线段的左限    int Node_Rpoint;//i点线段的右限
}LineTree[N*4];              //不是2n-1!!!不是2n-1!!!不是2n-1!!!  int Box[N];    // 盒子
int _max;
int _sum;int fmax(int a,int b)
{return a>b?a:b;
}void build(int i,int l,int r)//引入节点标和左右标
{LineTree[i].Node_Lpoint=l,LineTree[i].Node_Rpoint=r;if(l==r)          //如果左右相等,表示只有一个元素 LineTree[i].Node_Sum=LineTree[i].Node_Max=Box[l];  //节点记录该单元素     else    {     /* 递归构造左右子树 */   build(2*i,l,(l+r)/2);    build(2*i+1,(l+r)/2+1,r);         /* 回归时得到当前i节点的max信息 */    LineTree[i].Node_Max=fmax(LineTree[2*i].Node_Max,LineTree[2*i+1].Node_Max);/* 回归时得到当前i节点的sum信息 */  LineTree[i].Node_Sum=LineTree[2*i].Node_Sum+LineTree[2*i+1].Node_Sum;    }
}void updata(int i,int x,int y)/*单节点更新    y替换x */
{    if(LineTree[i].Node_Lpoint==x&&LineTree[i].Node_Rpoint==x)                  //如果为X格子 {LineTree[i].Node_Sum=LineTree[i].Node_Max=y;             //单格子更新 while(i)                              //递归更新其所有父级路径 { if(i%2){LineTree[i/2].Node_Sum=LineTree[i-1].Node_Sum+LineTree[i].Node_Sum;LineTree[i/2].Node_Max=fmax(LineTree[i-1].Node_Max,LineTree[i].Node_Max);}else{LineTree[i/2].Node_Sum=LineTree[i+1].Node_Sum+LineTree[i].Node_Sum;LineTree[i/2].Node_Max=fmax(LineTree[i+1].Node_Max,LineTree[i].Node_Max);}i/=2;}}else                                 //递归遍历 {i*=2;if(x>LineTree[i].Node_Rpoint)updata(i+1,x,y);elseupdata(i,x,y);}
}int readmax(int i,int x,int y)       //区间最大值查询
{   if(LineTree[i].Node_Lpoint==x&&LineTree[i].Node_Rpoint==y)_max=fmax(_max,LineTree[i].Node_Max);        //只有一个数                else{i*=2;if(x<=LineTree[i].Node_Rpoint)            //右区间    {if(y<=LineTree[i].Node_Rpoint)           //完全包含 readmax(i,x,y);else                  //半包含 readmax(i,x,LineTree[i].Node_Rpoint);}i++;if(y>=LineTree[i].Node_Lpoint)            //左区间 {if(x>=LineTree[i].Node_Lpoint)          //完全包含 readmax(i,x,y);else                  //半包含 readmax(i,LineTree[i].Node_Lpoint,y); }}
}
int readsum(int i,int x,int y)       //区间和 遍历同上
{   if(LineTree[i].Node_Lpoint==x&&LineTree[i].Node_Rpoint==y)_sum+=LineTree[i].Node_Sum;else                    {i*=2;if(x<=LineTree[i].Node_Rpoint){if(y<=LineTree[i].Node_Rpoint)readsum(i,x,y);elsereadsum(i,x,LineTree[i].Node_Rpoint);}i++;if(y>=LineTree[i].Node_Lpoint){if(x>=LineTree[i].Node_Lpoint)readsum(i,x,y);elsereadsum(i,LineTree[i].Node_Lpoint,y);}}
}   int main()
{int i,n,m,p,x,y;while(~scanf("%d%d",&n,&m)){for(i=1;i<=n;i++)scanf("%d",&Box[i]);build(1,1,n);       while(m--){scanf("%d%d%d",&p,&x,&y);switch(p){case 1:updata(1,x,y);break;case 2:_sum= 0;readsum(1,x,y);printf("%d\n",_sum);break;case 3:_max=-1;readmax(1,x,y);printf("%d\n",_max);break;}}}
}

注意!!这个问题也是困扰了我很久的一个问题,我们申请线段树时候,其空间到底是多少?

对于线段树来说,N也就是单独的树最终会成为其叶子节点,二叉树所需要的节点是2n-1,那么我们能否认为线段树所需要的空间即2n-1?

然而实时证明,这是错误的。

我们假设构造(1,11)这个线段并对其节点进行标注

(1,11)【节点1】
(1,5)  【节点2】(6,11)【节点3】
(1,3)  【节点4】(4,5)  【节点5】 (6,8)【节点6】(9,11)【节点7】
(1,2)  【节点8】(3)       【节点9】(4)      【节点10】(5)     【节点11】(6,7)【节点12】(8)【节点13】(9,10)【节点14】(11)【节点15】

我们看,节点14也就是(9,10)还需要递归扩展子树

他的子树应该是2n,2n+1,也就是28,29

咦?11个数不应该是21个节点吗?   其实是这样的,在之前的叶子节点(比如8,9)虽然没有子节点了,但是他仍然占有16,17,18,19的位置

为了满足我们的查找规律。所以,我们申请资源的时候不应该设为2n-1

线段树——操作格子(蓝桥杯试题集)相关推荐

  1. 油漆面积 线段树+线扫描 蓝桥杯 java

    X星球的一批考古机器人正在一片废墟上考古. 该区域的地面坚硬如石.平整如镜. 管理人员为方便,建立了标准的直角坐标系. 每个机器人都各有特长.身怀绝技. 它们感兴趣的内容也不相同. 经过各种测量,每个 ...

  2. 最短路径——SPFA算法(蓝桥杯试题集)

    *对于本题的floyd题解请跳转:http://blog.csdn.net/sm9sun/article/details/53285870 题目链接: http://lx.lanqiao.cn/pro ...

  3. 最小生成树——安慰奶牛(蓝桥杯试题集)

    *对prim算法.Kruskal算法不是很理解的请移步 http://blog.csdn.net/sm9sun/article/details/53256232  //并查集 http://blog. ...

  4. 动态规划——节点选择(蓝桥杯试题集)

    题目链接: http://lx.lanqiao.cn/problem.page?gpid=T14 问题描述 有一棵 n 个节点的树,树上每个节点都有一个正整数权值.如果一个点被选择了,那么在树上和它相 ...

  5. java蓝桥杯的题_Java蓝桥杯试题集——算法训练ALGO-116——最大的算式

    题目要求 解题思路 动态规划,今天才弄明白QAQ,借鉴了这位大佬的博客,曹磊的博客 写的很好!但是我觉得我的循环方式更容易理解嘿嘿嘿~ 首先建立如下图的数组,行数代表前几位数,列数代表有几个乘号.将第 ...

  6. 最短路径——Floyd算法及优化(蓝桥杯试题集)

    *对最短路径问题以及floyd算法.Dijkstra算法不是很理解的同学请移步前几篇博客~ 题目链接: http://lx.lanqiao.cn/problem.page?gpid=T15 问题描述 ...

  7. 动态规划——K号数(蓝桥杯试题集)

    题目链接:http://lx.lanqiao.cn/problem.page?gpid=T13 问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数.求 ...

  8. 动态规划——看似dp的贪心问题最大乘积(蓝桥杯试题集)

    题目链接: http://lx.lanqiao.cn/problem.page?gpid=T136 问题描述 对于n个数,从中取出m个数,如何取使得这m个数的乘积最大呢? 输入格式 第一行一个数表示数 ...

  9. 蓝桥杯研究生c语言试题答案,蓝桥杯试题_蓝桥杯 你有蓝桥杯历年的试题吗最好有参考答案啊 高职高专组C语言的 有的话麻烦你发给我 万分感谢_淘题吧...

    ❶ 蓝桥杯 你有蓝桥杯历年的试题吗最好有参考答案啊. 高职高专组C语言的 有的话麻烦你发给我 万分感谢 我有真题.但是老师没给答案 ❷ 为什么蓝桥杯试题集评测老是错 楼上的网友说的很简单,实际上因为每 ...

最新文章

  1. 第1章 Java语言概述
  2. Java设计模式(装饰者模式-组合模式-外观模式-享元模式)
  3. Windows内核HAL相关学习
  4. 【编程通识】正则表达式
  5. SQL转换函数to_char/to_date/to_number
  6. ArrayList为何线程不安全,如何解决
  7. 技术有温,代码有爱——1024技术公益信息无障碍
  8. 基于 ida 的反汇编转换 Obj 的可行性 笔记(1)
  9. mysql不被其他ip访问_mysql数据库无法被其他ip访问的解决方法
  10. .NET使用免费开源类库操作Excel
  11. 报表打印问题整体解决方案
  12. 2021总结和2022展望
  13. 饿了么UI组件库中,Upload组件上传闪动的解决
  14. T19136 交通指挥系统 题解
  15. Linux系统装intel网卡,在Centos下安装intel网卡的方法
  16. 小失误点,不积跬步无以至千里
  17. yudian温控表a1温度怎么补偿_yudian温控表a1说明书
  18. GNU/Linux 初学之旅(转)
  19. 修改Ubuntu的更新源
  20. 数字签名标准算法——DSA

热门文章

  1. 43 SD配置-销售凭证设置-定义状态管理授权码
  2. atxserver运行没有反应_连续生物工艺:灌流生物反应器
  3. 导航栏iframe公共样式_中秋节微信公众号推文样式素材推荐
  4. python中eval函数调用_如何从Python exec()/eval()调用中获取结果?
  5. PHP5异常处理,PHP5异常处理分析实例
  6. C++:利用sort()对vector中的数据自定义排序
  7. 【转】基于Token的身份验证原理
  8. jQuery-事件和应用
  9. PHP短网址链接在线生成源码 带后台
  10. Mouse Jiggler – 自动移动光标,防止电脑启动屏保、进入休眠[Windows]