蓝桥杯_算法训练_操作格子
2016-03-15 18:52
465 查看
这道题用到了数据结构中的线段树,没学习过线段树的同学可以去百度一下,我给你们推荐一下学习线段树的博客文章:/article/8132772.html
问题描述
有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。
问题描述
有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。
import java.util.Scanner; /** * @author 翔 * */ public class Main { private static int[] arr;//保存所有的格子数 private static int[][] op;//存放操作 private static int gridNum;//格子总数 private static int opNum;//操作种数 private static Node[] tree;//线段树 private static int[] father;//father[i]:表示第i个格子在线段树中的位置 private static int tempMax;//用于求区间最大值 private static int tempSum;//用于求区间权值和 /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub init();//初始化,包括输入参数、构建线段树 fun(); } private static void fun(){ for(int i=0;i<opNum;i++){ int p=op[i][0]; int x=op[i][1]; int y=op[i][2]; switch(p){ case 1:arr[x]=y;update(father[x]);break; case 2:tempSum=0;getSum(x,y,1);System.out.println(tempSum);break; case 3:tempMax=Integer.MIN_VALUE;getMax(x,y,1);System.out.println(tempMax);break; } } } // index为区间的序号(对应的区间是最大范围的那个区间,也是第一个图最顶端的区间,一般初始是 1 ) private static void getSum(int x,int y,int index){ if(x==tree[index].left&&y==tree[index].right){ tempSum+=tree[index].sum; return; } int leftIndex=index*2;//左子树在tree中的位置 if(x<=tree[leftIndex].right){//所求的区间在左子树中有涉及 if(y<=tree[leftIndex].right){//左子树完全包含所求的区间,则查询区间形态不变 getSum(x,y,leftIndex); }else{//半包含于左区间,则查询区间拆分,左端点不变,右端点变为左子树的右区间端点 getSum(x,tree[leftIndex].right,leftIndex); } } int rightIndex=leftIndex+1;//右子树在tree中的位置 if(y>=tree[rightIndex].left){//所求的区间在右子树有涉及 if(x>=tree[rightIndex].left){//右子树完全包含所求的区间,则查询区间形态不变 getSum(x,y,rightIndex); }else{// 半包含于右区间,则查询区间拆分,与上同理 getSum(tree[rightIndex].left,y,rightIndex); } } } // index为区间的序号(对应的区间是最大范围的那个区间,也是第一个图最顶端的区间,一般初始是 1) private static void getMax(int x,int y,int index){ if(x==tree[index].left&&y==tree[index].right){ tempMax=tree[index].max>tempMax?tree[index].max:tempMax; return; } int leftIndex=index*2;//左子树在tree中的位置 if(x<=tree[leftIndex].right){//所求的区间在左子树中有涉及 if(y<=tree[leftIndex].right){//左子树完全包含所求的区间,则查询区间形态不变 getMax(x,y,leftIndex); }else{//半包含于左区间,则查询区间拆分,左端点不变,右端点变为左子树的右区间端点 getMax(x,tree[leftIndex].right,leftIndex); } } int rightIndex=leftIndex+1;//右子树在tree中的位置 if(y>=tree[rightIndex].left){//所求的区间在右子树有涉及 if(x>=tree[rightIndex].left){//右子树完全包含所求的区间,则查询区间形态不变 getMax(x,y,rightIndex); }else{// 半包含于左区间,则查询区间拆分,与上同理 getMax(tree[rightIndex].left,y,rightIndex); } } } private static void update(int index){// 从下往上更新(注:这个点本身已经在函数外更新过了) if(index==1)return;// 向上已经找到了祖先(整个线段树的祖先结点 对应的下标为1) if(tree[index].left==tree[index].right){//该节点为叶子节点 tree[index].max=arr[tree[index].left]; tree[index].sum=arr[tree[index].left]; } int fatherIndex=index/2;//代表父节点在tree中的位置 tree[fatherIndex].max=tree[fatherIndex*2].max>tree[fatherIndex*2+1].max?tree[fatherIndex*2].max:tree[fatherIndex*2+1].max; tree[fatherIndex].sum=tree[fatherIndex*2].sum+tree[fatherIndex*2+1].sum; update(fatherIndex); } //初始化,包括输入参数、构建线段树 private static void init(){ Scanner sc=new Scanner(System.in); gridNum=sc.nextInt(); opNum=sc.nextInt(); arr=new int[gridNum+1]; op=new int[opNum][3]; tree=new Node[2*gridNum];//线段树 father=new int[gridNum+1];//father[i]:代表arr[i]在tree中的位置 for(int i=1;i<=gridNum;i++){ arr[i]=sc.nextInt();//初始化各格子数 } for(int i=0;i<opNum;i++){ for(int j=0;j<3;j++){ op[i][j]=sc.nextInt();//输入所有操作 } } buildTree(1,gridNum,1);//构造线段树 //更新区间最大值、区间和 for(int i=1;i<=gridNum;i++){ update(father[i]); } sc.close(); } //为区间[left,right]建立一个以index为祖先的线段树,index为数组下标 private static void buildTree(int left,int right,int index){ tree[index]=new Node(); tree[index].left=left; tree[index].right=right; if(left==right){// 当区间长度为 0 时,结束递归 father[left]=index;// 能知道某个点对应的序号,为了update()的时候从下往上一直到顶 return; } //该结点往 左孩子的方向 继续建立线段树,线段的划分是二分思想,如果写过二分查找的话这里很容易接受 ,这里将 区间[left,right] 一分为二了 buildTree(left,(int)Math.floor(((left+right)/2.0)),index*2); // 该结点往 右孩子的方向 继续建立线段树 buildTree((int)Math.floor(((left+right)/2.0))+1,right,index*2+1); } } class Node{ int left; int right; int sum; int max; }
相关文章推荐
- 青云、UCloud、阿里云、腾讯云特点
- 正则匹配以某字符串开始的整行
- 机器学习:逻辑回归python实现
- 样式问题-如何一次性设置网站英文字体样式,中文字体等样式
- DAX Calculate&SUMMARIZE 关键词的说明
- HDOJ-1066 敌兵布阵
- 文本深度表示模型Word2Vec
- C++ vector多维数组初始化及清零
- [Leetcode]Verify Preorder Serialization of a Binary Tree
- 做个东西学习并巩固SSH
- 头文件中定义const全局变量应注意的问题
- CF 295E Yaroslav and Points(Splay)
- WCF、WebAPI、WCFREST、WebService之间的区别【转】
- vsftpd.conf文件说明
- Java虚拟机结构
- 什么是Flat File
- 第3周项目2—本月有几天?(switch语句)
- html嵌套MP4、PDF的简单方案
- 第四章 使用Docker镜像和仓库
- Linux 特殊符号使用: 倒引号`的使用