CDOJ 844 线段树区间最大连续和

程序设计竞赛

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

“你动规无力,图论不稳,数据结构松散,贪心迟钝,没一样像样的,就你还想和我同台竞技,做你的美梦!今天这场比赛,就是要让你知道你是多么的无能!!”

不训练,无以为战。有 n n项能力是ACM竞赛要求的,训练则能提升,忽略则会荒废。

m m天,你能做到如何。

Input

第一行两个整数 n n m m,分别表示有 n n项能力要求,共有 m m天。

第二行 n n个整数,第 i i个整数 ai ai表示第 i i项能力的数值。

接下来 m m行,每行开始先读入一个整数 si si,表明这是一次询问还是一次能力变化。

si=0 si=0,表明这是一次询问,然后读入两个整数 li,ri li,ri,表示询问在 [liri] [li,ri]区间中任选一段连续序列,这段序列中所有能力值之和最大能是多少。

si=1 si=1,表明这是一次能力变化,然后读入两个整数 xi,wi xi,wi,表示第 xi xi项能力变为了 wi wi

1n,m100000,10000ai10000,1lirin,1xin,10000wi10000 1≤n,m≤100000,−10000≤ai≤10000,1≤li≤ri≤n,1≤xi≤n,−10000≤wi≤10000

Output

有多少询问就输出多少行,每行输出一个整数,作为对该询问的回答。

Sample input and output

Sample Input Sample Output
4 4
1 2 3 4
0 1 3
1 3 -3
0 2 4
0 3 3
6
4
-3
题目大意:
单点修改(1操作)+求区间最大连续和(0操作)

解题思路:
这道题原本是一道dp题目(?),我们在这里使用线段树解决。
维护一个线段树,其中包含:
l:左端点
r:右端点
sum:区间和
maxx:区间连续最大和
maxl:从l开始的连续最大和
maxr:以r结尾的连续最大和

现在考虑一个线段树结点:(o为当前节点,lo为左儿子,ro为右儿子)


sum直接用左右儿子的sum值更新,即tree[o].sum=tree[lo].sum+tree[ro].sum

maxl有两种状态可以更新

1、 左儿子的maxl值

2、 左儿子的sum值+右儿子的maxl值


因此,tree[o].maxl=max(tree[lo].maxl,tree[lo].sum+tree[ro].maxl);


同理,maxr也有两种状态

1、 右儿子的maxr值

2、 右儿子的sum值+左儿子的maxr值


因此,tree[o].maxr=max(tree[ro].maxr,tree[ro].sum+tree[lo].maxr);


但对于maxx,状态就复杂了

1、自己的maxl值

2、自己的maxr值

3、 左儿子的maxx值

4、右儿子的maxx值

5、左儿子的maxr值+右儿子的maxl值


因此,tree[o].maxx=max(tree[o].maxl,tree[o].maxr,tree[lo].maxx,tree[ro].maxx,tree[lo].maxr+tree[ro].maxl);


在区间求解的时候,我们也要做一些改动

考虑要求的区间[ql,qr],由于线段树的性质,递归后总会分成若干个前后端点连续的线段树节点:
 
又因为线段树区间的查找顺序是依次向右的,所以我们从ql开始可以依次更新它们
维护四个全局变量:
anss:当前已经更新的区间的和
maxans:当前已经更新的区间的最大连续和
ansl:当前已经更新的区间从左端(ql)开始的最大连续和
ansr:当前已经更新的区间以右端结束的最大连续和
每次更新类似于线段树的更新操作,详见代码(其实是博主不想画图了)

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define LL long long

const int N=100010;
const int MAX=1e9+7;
int n,m;
int a[N];
int ansl,ansr,anss,maxans;

inline void R(int &v)//读入、输出优化
{
	v=0;
	char ch=getchar();
	int f=0;
	while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
	while(isdigit(ch)){v=(v<<3)+(v<<1)+ch-'0';ch=getchar();}
	if(f) v=-v;
}
namespace ib {char b[100];}
inline void P(int x)
{
    if(x==0) {putchar(48); return;}
    if(x<0) {putchar('-'); x=-x;}
    char *s=ib::b;
    while(x) *(++s)=x%10, x/=10;
    while(s!=ib::b) putchar((*(s--))+48);
}
struct Segtree
{
	struct trie
	{
		int l,r,len;
		int sum;
		int maxl,maxr,maxx;
		int lz;
	}tree[N<<2];
	
	void updata(int o)//线段树更新 
	{
		int lo=o<<1,ro=o<<1|1;
		tree[o].sum=tree[lo].sum+tree[ro].sum;
		
		tree[o].maxl=max(tree[lo].maxl,tree[lo].sum+tree[ro].maxl);
		
		tree[o].maxr=max(tree[ro].maxr,tree[ro].sum+tree[lo].maxr);
		
		tree[o].maxx=tree[o].maxl;
		tree[o].maxx=max(tree[o].maxx,tree[o].maxr);
		tree[o].maxx=max(tree[o].maxx,tree[lo].maxx);
		tree[o].maxx=max(tree[o].maxx,tree[ro].maxx);
		tree[o].maxx=max(tree[o].maxx,tree[lo].maxr+tree[ro].maxl);
	}
	
	void build(int o,int l,int r)//建树 
	{
		tree[o].sum=0;
		tree[o].l=l;
		tree[o].r=r;
		tree[o].len=r-l+1;
		if(l==r) {tree[o].sum=tree[o].maxl=tree[o].maxr=tree[o].maxx=a[l];return;}
		int mid=(l+r)>>1;
		build(o<<1,l,mid);
		build(o<<1|1,mid+1,r);
		updata(o);
	}
	void query(int o,int ql,int qr)//区间和 
	{
		int l=tree[o].l,r=tree[o].r;
		if(ql<=l&&qr>=r) //更新查找的区间答案(注意顺序)
		{
			ansl=max(ansl,anss+tree[o].maxl);
			maxans=max(maxans,ansl);
			
			maxans=max(maxans,ansr+tree[o].maxl);
			maxans=max(maxans,tree[o].maxx);
			
			ansr=max(ansr+tree[o].sum,tree[o].maxr);
			maxans=max(maxans,ansr);
			
			anss+=tree[o].sum;
			return;
		}
		int mid=l+r>>1;
		if(qr<=mid)  query(o<<1,ql,qr);
		if(ql>mid)  query(o<<1|1,ql,qr);
		if(ql<=mid&&qr>mid)
		{
			query(o<<1,ql,mid);
			query(o<<1|1,mid+1,qr);
		}
	}
	
	void change(int o,int q,int v)//单点修改 
	{
		int l=tree[o].l,r=tree[o].r;
		if(l==r) {tree[o].sum=v,tree[o].maxl=v,tree[o].maxr=v,tree[o].maxx=v;return;}
		int mid=l+r>>1;
		if(q<=mid) change(o<<1,q,v);
		else change(o<<1|1,q,v);
		updata(o);
	}
	
}A;


int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	
	int i,j;
	R(n);R(m);
	for(i=1;i<=n;++i) R(a[i]);
	A.build(1,1,n);
	for(i=1;i<=m;++i)	
	{
		int x;
		R(x);
		if(x)
		{
			int q,v;
			R(q);R(v);
			A.change(1,q,v);
		}
		else
		{
			ansl=-MAX,ansr=-MAX,anss=0,maxans=-MAX;
			int l,r;
			R(l);R(r);
			A.query(1,l,r);
			P(maxans);
			puts("");
		}
	}
	return 0;
}
结语:
*线段树
非常巧妙的线段树题目,用四个值的相互转化维护了答案。
在转移时要注意顺序,顺序,顺序!



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值