操作格子——线段树

问题描述
有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。




本题直接用常规的方法肯定会超时,所以只能用线段树的方法

线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。
线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。
本题需要构造的线段树储存两个信息,一是区间内的和,二是区间内的最大值,当然为了写代码简便我们还需要在节点上储存上储存他们所代表的区间。
预处理耗时O(n),查询、更新操作O(logn),需要额外的空间O(n)。根据这个问题我们构造如下的二叉树
叶子节点是原始组数中的元素

非叶子节点储存两个信息一是它的所有子孙叶子节点所在区间的最大值,二是所有子孙节点所在区间内的和。


例如对于数组[2, 5, 1, 4, 9, 3](序号从一开始)可以构造如下的二叉树(背景为白色表示叶子节点,非叶子节点的值从前到后分别对应是其对应数组区间内的最大值和和,例如根节点表示数组区间[1-6]内的最大值是9,和是24)



      明白了结构对于线段树的插入,更新·,操作就和普通树差不多。我们可以定义一个结构体来描述树的每个节点,然后用结构体数组描述整棵树的情况,从1开始为根节点,若节点在数组中编号为i,则它的左孩子节点的编号为2i,右孩子节点标号为2i+1.


#include<iostream> 
#include<vector> 
#include<algorithm>
using namespace std;

int SUM,MAX;//用来记录所求区间内的和与最大值
struct SegTree
{
	int left,right,Max,sum;//分别节点所代表的左右区间,最大值,和
}seq[1000001];

void init(int l,int r,int i)//初始化l表示左区间,r表右区间,i表示自己的原序列编号
{
	seq[i].left=l;
	seq[i].right=r;
	seq[i].Max=0;
	seq[i].sum=0;
	if(l!=r)//如果没有到达叶子节点
	{
		int mid=(l+r)/2;
		init(l,mid,2*i);//递归初始化左子树
		init(mid+1,r,2*i+1);//递归初始化右子树
	}
}

void insert(int i,int x,int m)//插入操作,i表示根节点编号,x表示要插入的点在原序列的编号,m表示要插入点的值
{
	if(x>=seq[i].left&&x<=seq[i].right)//找到位置插入
	{
		seq[i].Max=m;
		seq[i].sum=m;
	}
	if(seq[i].left==seq[i].right)//找到叶节点返回
		return;
	int mid=(seq[i].left+seq[i].right)/2;
	if(x>mid)//如果序号在根的左区间,就把它往左子树上 
		insert(2*i+1,x,m);
	else//如果序号在根的右区间,就把它往右子树上插入
		insert(2*i,x,m);
	seq[i].sum=seq[2*i].sum+seq[2*i+1].sum;//每一次插入后更新一下根节点的sum与max值
	seq[i].Max=max(seq[2*i].Max,seq[2*i+1].Max);
}

int find_max(int x,int y,int i))//找最大值x,y表左右区间,i表节点编号
{
	if(x==seq[i].left&&y==seq[i].right)//如果找到就返回最大值return 
		return seq[i].Max;
	int mid=(seq[i].left+seq[i].right)/2;
	if(x>mid)//如果x,y区间序号在i节点的区间的右侧,就再从右区间里找
		return find_max(x,y,2*i+1);
	else if(y<=mid)
		return find_max(x,y,2*i);
	else//如果x,y在i节点的区间内,那么就得从i节点的左右孩子节点内找一个最大值
		return max(find_max(x,mid,2*i),find_max(mid+1,y,2*i+1));
}

int find_sum(int x,int y,int i)//与上面基本相同
{
	if(x==seq[i].left&&y==seq[i].right)
		return seq[i].sum;
	int mid=(seq[i].left+seq[i].right)/2;
	if(x>mid)
		return find_sum(x,y,2*i+1);
	else if(y<=mid)
		return find_sum(x,y,2*i);
	else
		return find_sum(x,mid,2*i)+find_sum(mid+1,y,2*i+1);
}
int main()  
{  
	
	int n,m,weight,p,x,y;
	cin>>n>>m;
	init(1,n,1);
	for(int i=1;i<=n;i++)
	{
		cin>>weight;
		insert(1,i,weight);
	}
	for(int i=0;i<m;i++)
	{
		cin>>p>>x>>y;
		if(p==1)
			insert(1,x,y);
		if(p==2)
			cout<<find_sum(x,y,1)<<endl;
		if(p==3)
			cout<<find_max(x,y,1)<<endl;	
	}	
	return 0;
}                                                 


请给我一些思路 ## 题目描述 你是歌剧中的一个小角色——塞维利亚的园丁。歌剧的舞台背景是一个由单位格组成的矩形庭院,共有 $\mathbf{R}$ 行 $\mathbf{C}$ 。你被要求在庭院中布置一组树篱迷宫:每个格子都必须放置一根对角树篱。对于任意一个格子,有两种可能的树篱类型:从左下右上(用 `/` 表示),或从左上右下(用 `\` 表示)。任何相邻的树篱相接处都会形成一堵连续的墙。 庭院外围有一圈单位格,宽度为一格,四个角格子缺失。每一个外围格子里都住着一位廷臣。外围格子的编号顺时针排,从顶行最侧的格子编号为 1,最后一个编号为 $2 \times (\mathbf{R}+\mathbf{C})$,即最顶端的格子。例如,当 $\mathbf{R}=2, \mathbf{C}=2$ 时,外围格子的编号如下(注意,此时还未放置树篱): ``` 12 8 3 7 4 65 ``` 在这个与众不同的歌剧中,爱情是互相且唯一的:每位廷臣只爱一位其他廷臣,且这份爱是双向且专属的。每位廷臣都希望能穿越树篱迷宫,悄悄地与心上人相会,并且不被其他廷臣遇见。也就是说,任意一对恋人廷臣之间,必须存在一条只属于他们两人的、被树篱墙与其他路径完全隔开的通路。迷宫中可以存在不属于任何廷臣路径的部分,只要所有恋人对都能连通即可。 给定所有恋人配对关系,你能否构造出这样一组树篱迷宫,使得每一对恋人都能连通?如无法实现,请输出 IMPOSSIBLE。 ## 输入格式 输入的第一行包含一个整数 $\mathbf{T}$,表示测试用例组数。接下来有 $\mathbf{T}$ 组测试用例,每组包含两行。第一行为两个整数 $\mathbf{R}$ 和 $\mathbf{C}$,表示庭院的行数和数。第二行为一个长度为 $2 \times (\mathbf{R}+\mathbf{C})$ 的排,包含所有廷臣的编号。第 1、2 个编号为一对恋人,第 3、4 个编号为一对恋人,以此类推。 ## 输出格式 对于每组测试用例,先输出一行 `Case #x:`,其中 $x$ 为测试用例编号(从 1 开始)。如果无法满足条件,再输出一行 IMPOSSIBLE。否则,输出 $\mathbf{R}$ 行,每行 $\mathbf{C}$ 个字符,表示一个合法的树篱迷宫,每个字符为 `/` 或 `\`。迷宫中的每个格子都必须填满,不能留空。若存在多种方案,你可任选其一输出。 ## 输入输出样例 #1 ### 输入 #1 ``` 4 1 1 1 4 3 2 1 3 1 8 2 7 3 4 5 6 2 2 8 1 4 5 2 3 7 6 1 1 1 3 2 4 ``` ### 输出 #1 ``` Case #1: / Case #2: //\ Case #3: // \/ Case #4: IMPOSSIBLE ``` ## 说明/提示 **样例解释** 在第 3 组中,恋人配对为 $(8, 1), (4, 5), (2, 3), (7, 6)$。如下是样例输出的示意图: ![](https://cdn.luogu.com.cn/upload/image_hosting/bxa3n9a8.png) 对于第 3 组,下面这种迷宫也是合法的: ``` /\ \/ ``` 在第 4 组中,庭院仅有一个格子,外围廷臣编号按顺时针分别为 1、2、3、4。此时只有两种放置方式:/ 或 \。第一种会形成 1 到 4、2 到 3 的通路,第二种会形成 1 到 2、3 到 4 的通路。但本组数据中 1 爱 3、2 爱 4,无论哪种方式都无法满足条件,因此输出 IMPOSSIBLE,歌剧中将充满悲伤的咏叹调! **限制条件** **小数据集(6 分,测试集 1 - 可见)** - $1 \leqslant \mathbf{T} \leqslant 100$。 - $1 \leqslant \mathbf{R} \times \mathbf{C} \leqslant 16$。 **大数据集(23 分,测试集 2 - 隐藏)** - $1 \leqslant \mathbf{T} \leqslant 500$。 - $1 \leqslant \mathbf{R} \times \mathbf{C} \leqslant 100$。
最新发布
11-06
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值