【线段树】操作格子

【线段树】操作格子

此题为线段树算法题,题目来源计科老班

题目描述

有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]内格子最大的权值。

数据规模与约定
对于20%的数据n < = 100,m < = 200。
对于50%的数据n < = 5000,m < = 5000。
对于100%的数据1 < = n < = 100000,m < = 100000,0 < = 格子权值 < = 10000。

输出

有若干行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。

样例输入

4 3
1 2 3 4
2 1 3
1 4 3
3 1 4

样例输出

6
3

思路详解

线段树
由于数据规模大,如果从头开始遍历搜索树,效率慢并且复杂度高。因此将n个数分段查询,这样可以提高效率。已知n个数,其划分的线段不会超过log2n+1。
如何构建线段树?
使用递归方法来建立线段树,先递归建好左子树,再递归建好右子树。由于题目需要找寻最大值和权值和,因此建树时就将最大值和权值和保存在树的节点中。

代码


import java.util.Scanner;

public class LinearTreeP1382 {
	int[] A;
	BTNode root;
	public LinearTreeP1382() {
		int n,m;
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		A = new int[n];
		for(int i = 0; i < n; i++)
			A[i] = sc.nextInt();
		root = Build(0, n-1);
		int p,x,y;
		
		for(int i=0;i<m;i++) {
			p = sc.nextInt();
			x = sc.nextInt();
			y = sc.nextInt();
			switch(p) {
				case 1:
					Update(x-1, y, root);
					break;
				case 2:
					System.out.println(Search(x-1, y-1, root));
					break;
				case 3:
					System.out.println(SearchMax(x-1, y-1, root));
					break;
			}
		}

	}
	
	BTNode Build(int start, int end) {			//建立线段树
		BTNode current = new BTNode();
		current.L = start;
		current.R = end;
		if(start == end) {
			current.sum = A[start];
			current.max = A[start];
			return current;
		}
		//不只一个数,有左右孩子
		int mid = (start+end)/2;
		current.left  = Build(start,mid);
		current.right = Build(mid+1, end);
		current.sum   = current.left.sum + current.right.sum;
		current.max=Math.max(current.left.max,current.right.max);
		return current;
	}
	//求权值和,p=2
	int Search(int start, int end, BTNode current) {
		if(start == current.L && end == current.R)
			return current.sum;
		int mid = (current.L+current.R)/2;
		//都在左孩子
		if(end <= mid) 			
			return Search(start, end, current.left);
		//都在右孩子
		if(start > mid) 			
			return Search(start, end, current.right);
		//一部分在左孩子,一部分在右孩子
		return Search(start, mid, current.left)
				+Search(mid+1, end, current.right);
	}
	//更新权值,p=1
	void Update(int index,int newvalue, BTNode current) {
		if(current.L == index && current.R == index) {
			current.sum=newvalue;
			current.max=newvalue;
			return;
		}
		int mid = (current.L+current.R)/2;
		if(index <= mid)
			Update(index, newvalue, current.left);
		else
			Update(index, newvalue, current.right);
		
		current.sum = current.left.sum + current.right.sum;		//更新权值和
		current.max=Math.max(current.left.max,current.right.max);	//更新最大值
	}
	//求取最大值,p=3
	int SearchMax(int start, int end, BTNode current) {
		if(start == current.L && end == current.R)
			return current.max;
		int mid = (current.L+current.R)/2;
		
		if(end <= mid)
			return SearchMax(start, end, current.left);
		
		if(start > mid)
			return SearchMax(start, end, current.right);
		
		return Math.max(SearchMax(start, mid, current.left), 
				SearchMax(mid+1, end, current.right));	
			
	}

	public static void main(String[] args) {
		LinearTreeP1382 l = new LinearTreeP1382();

	}
	
	class BTNode{		//线段树节点定义
		int    L,R;		//L开始下标,R结束下标 
		int    sum;
		int	   max;
		BTNode left;
		BTNode right;
	}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值