UESTC 844 程序设计竞赛 【维护值稍多的线段树】

【题目大意】

维护一个序列,使之支持一下两种操作:

1、单点修改

2、查询一个区间子区间最大值(在某个区间中任意寻找一段连续子区间,使得该子区间的和最大)(以下将该值称为‘答案’)

【解题思路】

线段树做法:

考虑维护四种和:sum,suml,sumr,subsum,分别是区间和,左端点开始连续最大和(以下将该值称为‘左始和’),右端点结束连续最大和(以下将该值称为‘右结和’),答案

则区间和=左儿子和+右儿子和

左始和=max(左儿子左始和,左儿子区间和+右儿子左始和)

右结和=max(右儿子右结和,右儿子区间和+左儿子右结和)

答案=max(左始和,右结和,左儿子答案,右儿子答案,左儿子右结和+右儿子左始和)

图解:


所有信息维护好之后,由底层至上层最后统计一次答案(可以用一个队列实现)

【代码】(抱歉之前用栈写挂了改成队列没改名字,所以stk变量名可能有点误导)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cctype>
#include<iomanip>
#include<stack>
#include<queue>
#define LL long long
//#define LOCAL
using namespace std;

const int N=100111;
int n,m;

struct Seg_Tree{
	struct treenode{
		int l,r;
		int sum,suml,sumr,subsum;
		int lazy;
		void Update(int v){
			sum=suml=sumr=subsum=v;
		}
	}tree[N<<2];
	
	queue <int> stk;
	
	void Maintain(int o){
		int i=o;
		tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
		tree[o].suml=max(tree[o<<1].suml,tree[o<<1].sum+tree[o<<1|1].suml);
		tree[o].sumr=max(tree[o<<1|1].sumr,tree[o<<1|1].sum+tree[o<<1].sumr);
		tree[o].subsum=max(tree[o].suml,max(tree[o].sumr,max(tree[o<<1].subsum,
		max(tree[o<<1|1].subsum,tree[o<<1].sumr+tree[o<<1|1].suml))));

	}
	
	void Build(int o,int l,int r){
		tree[o].l=l;
		tree[o].r=r;
		tree[o].subsum=0;
		tree[o].sum=0;
		tree[o].suml=0;
		tree[o].sumr=0;
		if (l==r) return;
		else{
			int mid=(l+r)>>1;
			Build(o<<1,l,mid);
			Build(o<<1|1,mid+1,r);
		}
	}
	
	void Modify(int o,int pos,int v){
		int l=tree[o].l;
		int r=tree[o].r;
		if (l==pos&&r==pos) tree[o].Update(v);
		else{
			int mid=(l+r)>>1;
			if (pos<=mid) Modify(o<<1,pos,v);
			else Modify(o<<1|1,pos,v);
			Maintain(o);
		}
	}
	
	void Anti(int o,int ql,int qr){
		int l=tree[o].l;
		int r=tree[o].r;
		if (ql<=l&&qr>=r) stk.push(o);
		else{
			int mid=(l+r)>>1;
			if (ql<=mid) Anti(o<<1,ql,qr);
			if (qr>mid) Anti(o<<1|1,ql,qr);
		}
	}
	
	int Query(){
		int ans,ansl,ansr,subans;
		ans=tree[stk.front()].sum;
		ansl=tree[stk.front()].suml;
		ansr=tree[stk.front()].sumr;
		subans=tree[stk.front()].subsum;
		stk.pop();
		while (!stk.empty()){
			int head=stk.front();
			stk.pop();
			int tmp=ansr;
			ansl=max(ansl,ans+tree[head].suml);
			ansr=max(tree[head].sumr,tree[head].sum+ansr);
			subans=max(subans,max(tree[head].subsum,max(ansl,max(ansr,tmp+tree[head].suml))));
			ans+=tree[head].sum;
		}
		return subans;
	}
}SgTree;

int main(){
#ifdef LOCAL
    freopen("UESTC844.in","r",stdin);
#endif
    while (scanf("%d%d",&n,&m)!=EOF){
    	SgTree.Build(1,1,n);
		for (int i=1;i<=n;++i){
    		int x;
    		scanf("%d",&x);
    		SgTree.Modify(1,i,x);
		}
		while (m--){
			while (!SgTree.stk.empty()) SgTree.stk.pop();
			int sign;
			scanf("%d",&sign);
			if (sign){
				int x,y;
				scanf("%d%d",&x,&y);
				SgTree.Modify(1,x,y);
			}
			else{
				int x,y;
				scanf("%d%d",&x,&y);
				while (!SgTree.stk.empty()) SgTree.stk.pop();
				SgTree.Anti(1,x,y);
				printf("%d\n",SgTree.Query());
			}
		}
	}
}



【总结】

此题的维护值略多,需要仔细处理

听说也可以用动归做,回去再研究一下~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值