小白逛公园

小白逛公园

Time Limit:20000MS  Memory Limit:65536K
Case Time Limit:2000MS

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

Source

vijos



#include <stdio.h>
#define maxn 500000
#define inf -2100000000
inline void _read(int& d)
{
	char t=getchar();bool f=false;
	while(t<'0'||t>'9') {if(t=='-') f=true; t=getchar();}
	for(d=0;t<='9'&&t>='0';t=getchar()) d=d*10+t-'0';
	if(f) d=-d;
}
inline void _out(int d)
{
	int o[30],top=1;
	if(d==0){putchar('0');return ;}
	if(d<0) {putchar('-');d=-d;}
	while(d)
	{
		o[top++]=d%10;
		d/=10;
	}
	for(--top;top;--top) putchar('0'+o[top]);
}
struct node{int a, b, l, r, lmax, rmax, max, sum;}tree[maxn*2+9];
int mark[maxn+9], tot=0;
int rofmax2(int a, int b)
{
	if(a>b)	return a;
	return b;
}
int rofmax3(int a, int b, int c)
{
	return rofmax2(rofmax2(a, b), c);
}
int Min(int a, int b)
{
	if(a<b)	return a;
	return b;
}
void updata(int now)
{
	//以下全部维护当前区间的数据 
	int ls=tree[now].l, rs=tree[now].r;
	tree[now].sum=tree[ls].sum+tree[rs].sum;	//维护区间和 
	tree[now].lmax=rofmax2(tree[ls].lmax, tree[ls].sum+tree[rs].lmax);	//维护左起最长连续和 
	tree[now].rmax=rofmax2(tree[rs].rmax, tree[rs].sum+tree[ls].rmax);	//维护右起最长连续和 
	tree[now].max=rofmax3(tree[ls].max, tree[rs].max, tree[ls].rmax+tree[rs].lmax);	//维护最长区间和 
}
void maketree(int x, int y)
{
	int now=++tot;
	tree[now].a=x;	tree[now].b=y;
	tree[now].lmax=tree[now].rmax=tree[now].max=tree[now].sum=inf;
	if(x==y)
	{
		tree[now].l=0;
		tree[now].r=0;
		tree[now].max=tree[now].lmax=tree[now].rmax=tree[now].sum=mark[x];
		return;
	}
	tree[now].l=tot+1;
	maketree(x, (x+y)/2);
	tree[now].r=tot+1;
	maketree((x+y)/2+1, y);
	updata(now); 
}
int _search_right(int i, int l, int r)
{
    int t=-2100000000;
    if(l<=tree[i].a)	return tree[i].rmax;
	if(tree[i].r)t=rofmax2(t, _search_right(tree[i].r, l, r));   
	if(tree[i].l&&l<=tree[tree[i].l].b)t=rofmax2(t,tree[tree[i].r].sum+_search_right(tree[i].l, l, r));
    return t;
}
int _search_left(int i, int l, int r)
{
    int t=-2100000000;
    if(r>=tree[i].b)return	tree[i].lmax;	//如果当前节点包含于[l, r], 返回当前节点的左起最大连续和 
    if(tree[i].l)t=rofmax2(t, _search_left(tree[i].l, l, r));
	if(tree[i].r&&r>=tree[tree[i].r].a)t=rofmax2(t, tree[tree[i].l].sum+_search_left(tree[i].r, l, r));
    return t;
}
int getans(int p, int l, int r)
{
	if(l<=tree[p].a&&tree[p].b<=r)	return tree[p].max;		//如果当前节点包含于[l, r], 返回当前节点的最长连续和 
	int mid=(tree[p].a+tree[p].b)/2, lmax=inf, rmax=inf;	 
	if(l<=mid)	lmax=getans(tree[p].l, l, r);	//左儿子与[l, r]区间相交部分的最长连续和 
	if(r>mid)	rmax=getans(tree[p].r, l, r);	//有儿子与[l, r]区间相交部分的最长连续和 
	int lt=tree[p].l, rt=tree[p].r;
	int lenm=-2100000000;
	if(l<=tree[lt].b&&tree[rt].a<=r)	//如果左右儿子都与[l, r]相交, 
	{
		lenm=
		_search_left(rt, l, r)			//固定左边界向右搜索最大连续和 
		+
		_search_right(lt, l, r);		//固定右边界向左搜索最大连续和 
	}
	return rofmax2(lmax, rofmax2(rmax, lenm));
}
void change(int now, int k, int s)	//第k个公园的分数变为s,当前讨论到 now 点 
{
	if(tree[now].a<=k&&k<=tree[now].b)	//如果第k个公园在当前节点表示的区间之内 
	{
		if(tree[now].a==tree[now].b)	//如果当前节点是叶节点 
		{
			tree[now].lmax=tree[now].rmax=tree[now].max=tree[now].sum=s;
		}
		else	//如果当前节点不是叶节点 
		{
			change(tree[now].l, k, s);
			change(tree[now].r, k, s);
			updata(now);	
		}
	}
}
int main()
{
	int n, m, alt, a, b, p, s;
	_read(n);_read(m);			//n个公园,m个操作 
	for(int i=1; i<=n; i++)		//读入每个公园的分数 
	{
		_read(mark[i]);			 
	}
	maketree(1, n);				//建树 
	for(int i=1; i<=m; i++)
	{
		_read(alt);
		if(1==alt)
		{
			_read(a);	_read(b);	//选择公园的范围[a, b] 
			if(a>b)					//有可能a>b, 交换a,b 
			{
				int t=a;
				a=b;
				b=t;
			}
			_out(getans(1, a, b));	//输出[a, b]最长连续和 
			putchar('\n');
		}
		else
		{
			_read(p);	_read(s);	
			change(1, p, s);		//第p个公园的分变成s 
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值