bzoj1756 Vijos1083 小白逛公园

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

有一点坑的线段树……你需要特殊的写代码技巧

每个线段树节点要保存几个数据:lm从左节点往右能取到的最大值,rm从右节点往左能取到的最大值。ans保存当前区间任取一段的最大值

因为是单点更新,所以连lazy tag也不需要,精华在于询问和标记上传的函数


上传标记的时候要更新好多东西:

区间和tot:最简单,左边加右边

lm:可以取左边的一整段加右边的lm,或者只取左边的lm。lm=max(左边的tot+右边的lm,左边的lm)

rm:可以取右边的一整段加左边的rm,或者只取右边的rm。rm=max(右边的tot+左边的rm,右边的rm)

ans:可以取更新完的当前的lm、rm,或者取左边的rm加右边的lm


然后询问的时候函数的返回值要是一个线段树的节点而不只是一个数据答案

因为从左右转移的时候,你不知道这个答案是从左边的rm还是ans转移而来,右边也是

具体来说,如果左边从rm转移而来,那么它可以和右边的lm合并,而ans不能保证会“紧贴“在区间的右边,就不能和右边的lm合并

所以要返回一个线段树的节点,然后一切都简单了

#include<cstdio>
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void swap(int &a,int &b){int t=a;a=b;b=t;}
inline int max(int a,int b){if (a>b)return a;else return b;}
struct trees{int l,r,ls,rs,lm,rm,ans,tot;}tree[2000001];
int n,m,a[500001],treesize;
inline void update(int k)
{
	int ll=tree[k].ls,rr=tree[k].rs;
	tree[k].tot=tree[ll].tot+tree[rr].tot;
	tree[k].lm=max(tree[ll].lm,tree[ll].tot+tree[rr].lm);
	tree[k].rm=max(tree[rr].rm,tree[rr].tot+tree[ll].rm);
	tree[k].ans=max(max(tree[ll].ans,tree[rr].ans),tree[ll].rm+tree[rr].lm);
}
inline void buildtree(int l,int r)
{
	if (l>r) return;
	int now=++treesize;
	tree[now].l=l;tree[now].r=r;
	if (l==r)
	{
		tree[now].tot=tree[now].lm=tree[now].rm=tree[now].ans=a[l];
		return;
	}
	int mid=(l+r)>>1;
	tree[now].ls=treesize+1;
	buildtree(l,mid);
	tree[now].rs=treesize+1;
	buildtree(mid+1,r);
	update(now);
}
inline trees ask(int k,int l,int r)
{
	int x=tree[k].l,y=tree[k].r;
	if (l==x&&r==y) return tree[k];
	int mid=(x+y)>>1,ll=tree[k].ls,rr=tree[k].rs;
	if(r<=mid) return ask(ll,l,r);
	else if (l>mid) return ask(rr,l,r);
	else
	{
		trees t_ans;
		trees t1=ask(ll,l,mid);
		trees t2=ask(rr,mid+1,r);
		t_ans.ans=max(max(t1.ans,t2.ans),t1.rm+t2.lm);
		t_ans.rm=max(tree[rr].rm,tree[rr].tot+t1.rm);
		t_ans.lm=max(tree[ll].lm,tree[ll].tot+t2.lm);
		return t_ans;
	}
}
inline void change(int k,int to,int dat)
{
	int x=tree[k].l,y=tree[k].r;
	if (x==y)
	{
		tree[k].tot=tree[k].lm=tree[k].rm=tree[k].ans=dat;
		return;
	}
	int mid=(x+y)>>1;
	if (to<=mid)change(tree[k].ls,to,dat);
	if (to>mid)change(tree[k].rs,to,dat);
	update(k);
}
int main()
{
	n=read();m=read();
	for (int i=1;i<=n;i++)
	a[i]=read();
	buildtree(1,n);
	for (int i=1;i<=m;i++)
	  {
	  	int x=read(),y=read(),z=read();
	  	if (x==1)
	  	{
	  		if (y>z) swap(y,z);
	  		printf("%d\n",ask(1,y,z).ans);
	  	}else change(1,y,z);
	  }
	return 0;
}


已标记关键词 清除标记
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试题; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页