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;
}