[蓝桥杯]算法训练 操作格子-链式线段树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_19895789/article/details/71344145
问题描述

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

 

题目跳转链接

这题直接模拟的话100%的数据是过不去的,需要用线段树把最大值和和值储存起来。

关于线段树的资料和此题用数组线段树的解法网上都能搜到了,在此不做复述

但是网上我并没有看到有用链式线段树写法的,所以再次提供另一种思路

不过因为此题是满二叉树(关于满二叉树,国内的定义一般是指有2^n-1个节点,国际的定义一般是要么有两个子节点,要么是叶子,此处所说的是国际的定义版本),所以用数组线段树的写法其实更好,这里只供大家换换思路而已

注:事后发现这样写代码真的很烂,最新线段树算写的比较好的https://blog.csdn.net/qq_19895789/article/details/71512850

#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
	int l, r;//左右区间 
	node *_l, *_r,*_p;//左右父树 
	long long s;//和 
	int m;//最大 
};
int a[100001];//初始值 
node *_w[100001];//存最底层单个数字的情况,不要也可以,因为树不高,可以搜索 
node* bulid(int l,int r,node *_p)//建树 
{
	node *_tmp=new node;
	_tmp->_p = _p;
	_tmp->l = l;
	_tmp->r = r;
	if (l==r)//单个数字的情况 
	{
		_tmp->s=_tmp->m=a[l];
		_tmp->_l = _tmp->_r = NULL;
		_w[l] = _tmp;
		return _tmp;
	}
	int mid = (l + r) / 2;//其他情况 
	_tmp->_l = bulid(l, mid, _tmp);
	_tmp->_r = bulid(mid + 1, r, _tmp);
	_tmp->s = _tmp->_l->s + _tmp->_r->s;
	_tmp->m = max(_tmp->_l->m, _tmp->_r->m);
	return _tmp;
}
node* query(int l, int r, node *_cur)//查找,关于注释的代码后面会说 
{
//	node *_tmp=new node;
	if (l == _cur->l&&r == _cur->r)
	{
//		_tmp->s = _cur->s;
//		_tmp->m = _cur->m;
		return _cur;
	}
	int mid = (_cur->l + _cur->r) / 2;
	if (r <= mid)
	{
//		_tmp = query(l, r, _cur->_l);
//		return _tmp;
		return query(l, r, _cur->_l);
	}
	if (l > mid)
	{
//		_tmp = query(l, r, _cur->_r);
//		return _tmp;
		return query(l, r, _cur->_r);
	}
	node *_tmp=new node;
	node *_nl, *_nr;
	_nl = query(l, mid, _cur->_l);
	_nr = query(mid + 1, r, _cur->_r);
	_tmp->s = _nl->s + _nr->s;
	_tmp->m = max(_nl->m, _nr->m);
	return _tmp;//把和与最大值一起返回,这样可以只写一个查找函数 
}
void update(node *_tmp)//更新 
{
	int nows=_tmp->s,nowm=_tmp->m;
	_tmp->s = (_tmp->_l == NULL ? 0 : _tmp->_l->s) + (_tmp->_r == NULL ? 0 : _tmp->_r->s);
	_tmp->m = max(_tmp->_l == NULL ? 0 : _tmp->_l->m, _tmp->r == NULL ? 0 : _tmp->_r->m);
	if(nows==_tmp->s&&nowm==_tmp->m)//有点多余。 可以不要 
		return;
	if (_tmp->_p != NULL)
		update(_tmp->_p);
}
int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	node *_root=bulid(1,n,NULL);
	for (int i = 0; i < m; i++)
	{
		int type,x,y;
		cin >> type>>x>>y;
		switch (type)//操作 
		{
			case 1://从最底层开始往父节点更新 
				_w[x]->s = _w[x]->m = y;
				if (_w[x]->_p != NULL)
					update(_w[x]->_p);
				break;
			case 2:
				cout << query(x, y, _root)->s<<"\n";
				break;
			case 3:
				cout << query(x, y, _root)->m<<"\n";
				break;
		}
	}
	return 0;
}


关于那段注释掉的代码呢

一开始我没注释,用的是那个写法,提交到蓝桥这题是能过的,后来老师把这题放到校内OJ上,但内存只给了128M,这题就过不去了

查找时我新建了个用于储存值的指针,但是c++这里的是指针,函数结束后并不会清除占用内存,所以我把它注释掉了,只在下面没查找到指定的时候才申请,就能过校内OJ上的128MB内存版了



当然,我这套写法其实优化的地方很大。。也回到了最开始我说的其实这里用数组线段树写会更好

不过这里只是给大家提供一个思路,这题能AC我也懒得再优化了,下次遇到新的变形题的时候再做吧




猜你喜欢

转载自blog.csdn.net/qq_19895789/article/details/71344145
今日推荐