有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操作的结果。
1 2 3 4
2 1 3
1 4 3
3 1 4
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我也懒得再优化了,下次遇到新的变形题的时候再做吧