小白逛公园 --线段树

Description

小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。 
一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。 
那么,就请你来帮小白选择公园吧。

Input

第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。 
接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。 
接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,小新要带小白出去玩,接下来的两个整数a和b给出了选择公园的范围(1≤a,b≤N);K=2表示,小白改变了对某个公园的打分,接下来的两个整数p和s,表示小白对第p个公园的打分变成了s(1≤p≤N)。 
其中,1≤N≤500 000,1≤M≤100 000,所有打分都是绝对值不超过1000的整数。

Output

小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

Sample Input

5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3

Sample Output

2
-1


【分析】

朴素线段树显然是不能完成任务的,我们需要在朴素线段树中添加一些域来完成我们需要的查找任务。
对于每个节点i:
    首先我们肯定需要记录子树的最大值mv,也就是子树覆盖区间的答案
    由于一个答案区间可能是多个节点的合并,需要在查找的时候合并左右子树的答案,
所以我们必须记录从节点左端点向右(不超过右端点)的最大值lv,右端点向左(不超过左端点)的最大值rv
当然还需要当前区间的和sum


一步一步考虑:

1.初始化:
1.1.对于叶子节点,sum,mv,lv,rv都是当前位置的值
1.2.对于非叶子节点,sum=sum[ls]+sum[rs];
                    lv=max(lv[ls],sum[ls]+lv[rs]);

                    rv=max(rv[rs],sum[rs]+rv[ls])

                    mv=max(mv[ls],mv[rs],rv[ls]+lv[rs])


2.修改:
2.1.对于叶子节点,直接和初始化的方法一样
2.2.对于非叶子节点,在修改完左右儿子之后再修改自己,也和初始化一样


3.查询:
3.1.如果当前区间完全被覆盖在查询区间,直接返回mv
3.2.如果左右儿子与查询区间有交集,递归查询得到答案t1,t2;
3.3.查询恰好“骑”在中间的情况(即l<=Mid&&Mid+1<=r),也就是
固定右端点向左查询+固定左端点向右查询,得到答案t3;

3.4.固定左端点向右查询只需递归查询到左儿子的答案或者sum[ls]+右儿子的答案;

    固定右端点向左查询同理。
3.5.返回max(t1,t2,t3);

思路就这样,时间复杂度O(Q*(logN)^2)


【代码】

/*****************************
    ID:Ciocio
	LANG:C++
	DATE:2013-12-20
	TASK:Small White
*****************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

#define MAXN 500005
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
#define INF 999999999

struct node{
	int lv,rv,mv,sum;
	void push(int _x)
	{lv=rv=mv=sum=_x;}
}Tree[MAXN<<2];
int N,M,v[MAXN];

inline void _read(int &x,bool mark=0)
{
	char tt=getchar();
	while(tt<'0'||'9'<tt){if(tt=='-') mark=1;tt=getchar();}
	for(x=0;'0'<=tt&&tt<='9';x=(x<<3)+(x<<1)+tt-'0',tt=getchar());
	x=mark?-x:x;
}

inline int max(int a,int b,int c){return max(a,max(b,c));}

void _build(int id,int Left,int Right)                     //初始化
{
	int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
	if(Left==Right){Tree[id].push(v[Left]);return;}     //叶子节点
	
	_build(ls,Left,Mid);                     //非叶节点
	_build(rs,Mid+1,Right);
	
	Tree[id].sum=Tree[ls].sum+Tree[rs].sum;
	
	Tree[id].lv=max(Tree[ls].lv,Tree[ls].sum+Tree[rs].lv);
	Tree[id].rv=max(Tree[rs].rv,Tree[rs].sum+Tree[ls].rv);
	Tree[id].mv=max(Tree[ls].mv,Tree[rs].mv,Tree[ls].rv+Tree[rs].lv);
}

void _init()
{
	_read(N);_read(M);
	for(int i=1;i<=N;i++) _read(v[i]);
	_build(1,1,N);
}

int _getr(int id,int Left,int Right,int loc)                 //固定右端点向左查询,loc为左端点下界
{
	int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
	if(loc<=Left) return Tree[id].rv;
	if(Mid+1<=loc) return _getr(rs,Mid+1,Right,loc);
	return max(Tree[rs].sum+_getr(ls,Left,Mid,loc),Tree[rs].rv);
}

int _getl(int id,int Left,int Right,int loc)             //同上
{
	int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
	if(Right<=loc) return Tree[id].lv;
	if(loc<=Mid) return _getl(ls,Left,Mid,loc);
	return max(Tree[ls].sum+_getl(rs,Mid+1,Right,loc),Tree[ls].lv);
}

int _query(int id,int Left,int Right,int l,int r)             
{
	if(l<=Left&&Right<=r) return Tree[id].mv;
	int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
	int rtn=-INF;
	
	if(!(r<Left||Mid<l)) rtn=max(rtn,_query(ls,Left,Mid,l,r));
	if(!(r<Mid+1||Right<l)) rtn=max(rtn,_query(rs,Mid+1,Right,l,r));
		
	if(l<=Mid&&Mid+1<=r) rtn=max(rtn,_getr(ls,Left,Mid,l)+_getl(rs,Mid+1,Right,r));    //若查询区间与左右儿子均有交集
	return rtn;
}

void _change(int id,int Left,int Right,int loc,int val)
{
	if(loc<=Left&&Right<=loc)
	{Tree[id].push(val);return;}
	int ls=lson(id),rs=rson(id),Mid=(Left+Right)>>1;
	
	if(loc<=Mid) _change(ls,Left,Mid,loc,val);
	if(Mid<loc) _change(rs,Mid+1,Right,loc,val);
		
	Tree[id].sum=Tree[ls].sum+Tree[rs].sum;
	
	Tree[id].lv=max(Tree[ls].lv,Tree[ls].sum+Tree[rs].lv);
	Tree[id].rv=max(Tree[rs].rv,Tree[rs].sum+Tree[ls].rv);
	Tree[id].mv=max(Tree[ls].mv,Tree[rs].mv,Tree[ls].rv+Tree[rs].lv);
}

void _solve()
{
	int p,a,b;
	while(M--)
	{
		_read(p);_read(a);_read(b);
		if(p==1)
		{
			if(a>b) swap(a,b);         //坑爹的地方!!!
			printf("%d\n",_query(1,1,N,a,b));
		}
		else _change(1,1,N,a,b);
	}
}

int main()
{
	_init();
	_solve();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值