线段树之最大子段和(SPOJ三题)

心得

昨天杭电不会线段树维护最大子段和,补了一下,发现和维护最长01的区间合并是一样的

顺便网上搜了一下子段和的SPOJ的GSS1到GSS8,觉得还挺好的,决定补一下

其中GSS1、GSS2(坑题待补)、GSS3、GSS5是最大子段和,GSS4是区间开根(之前做过就不管了)

GSS6、GSS7、GSS8两个是平衡树(treap/splay),一个是树剖,对于目前来说,太难待补

 

SPOJ - GSS1 Can you answer these queries I(裸题)

You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ).
A query is defined as follows: 
Query(x,y) = Max { a[i]+a[i+1]+...+a[j] ; x ≤ i ≤ j ≤ y }. 
Given M queries, your program must output the results of these queries.

int范围,非空序列,没有修改,每次问区间[l,r]最大子段和,模板题

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,a[N],l,r;
struct node
{
	int lmx,rmx,mx,sum;
}e[N*4];
void pushup(node &fa,node &lson,node &rson)
{
	fa.sum=lson.sum+rson.sum;
	fa.lmx=max(lson.lmx,lson.sum+rson.lmx);
	fa.rmx=max(rson.rmx,rson.sum+lson.rmx);
	fa.mx=max(max(lson.mx,rson.mx),lson.rmx+rson.lmx);
}
void build(int p,int l,int r) 
{
	e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=0;
	if(l==r)
	{
		e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(e[p],e[p<<1],e[p<<1|1]);
}
void update(int p,int l,int r,int x,int v)
{
	if(l==r)
	{
		e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=v;
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid)update(p<<1,l,mid,x,v);
	else update(p<<1|1,mid+1,r,x,v);
	pushup(e[p],e[p<<1],e[p<<1|1]);
}
node ask(int p,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)return e[p];
	int mid=(l+r)/2;
	if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
	if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
	node ls=ask(p<<1,l,mid,ql,qr),rs=ask(p<<1|1,mid+1,r,ql,qr),ans;
	pushup(ans,ls,rs);
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",ask(1,1,n,l,r).mx);
	}
	return 0;
}

SPOJ - GSS3 Can you answer these queries III(裸题)

You are given a sequence A of N (N <= 50000) integers between -10000 and 10000.
On this sequence you have to apply M (M <= 50000) operations: 
modify the i-th element in the sequence
for given x y print max{Ai + Ai+1 + .. + Aj | x<=i<=j<=y }.

int范围,单点修改,每次询问[l,r]非空序列子段和(即负的仍输出负的)

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,a[N],op,x,y;
struct node
{
	int lmx,rmx,mx,sum;
}e[N*4];
void pushup(node &fa,node &lson,node &rson)
{
	fa.sum=lson.sum+rson.sum;
	fa.lmx=max(lson.lmx,lson.sum+rson.lmx);
	fa.rmx=max(rson.rmx,rson.sum+lson.rmx);
	fa.mx=max(max(lson.mx,rson.mx),lson.rmx+rson.lmx);
}
void build(int p,int l,int r) 
{
	e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=0;
	if(l==r)
	{
		e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(e[p],e[p<<1],e[p<<1|1]);
}
void update(int p,int l,int r,int x,int v)
{
	if(l==r)
	{
		e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=v;
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid)update(p<<1,l,mid,x,v);
	else update(p<<1|1,mid+1,r,x,v);
	pushup(e[p],e[p<<1],e[p<<1|1]);
}
node ask(int p,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)return e[p];
	int mid=(l+r)/2;
	if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
	if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
	node ls=ask(p<<1,l,mid,ql,qr),rs=ask(p<<1|1,mid+1,r,ql,qr),ans;
	pushup(ans,ls,rs);
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]);
	build(1,1,n);
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d%d%d",&op,&x,&y);
		if(op==0)update(1,1,n,x,y);
		else printf("%d\n",ask(1,1,n,x,y).mx);
	}
	return 0;
}

SPOJ - GSS5 Can you answer these queries V(分类讨论)

You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| <= 10000 , 1 <= N <= 10000 ).
A query is defined as follows:
Query(x1,y1,x2,y2) = Max { A[i]+A[i+1]+...+A[j] ; x1 <= i <= y1 , x2 <= j <= y2 and x1 <= x2 , y1 <= y2 }.
Given M queries (1 <= M <= 10000), your program must output the results of these queries.

给定x1,y1,左端点L在[x1,y1]间任取,给定x2,y2,右端点R在[x2,y2]间任取,

保证x1<=x2,y1<=y2,但有区间重合和区间包含覆盖的情况,问[L,R]的最大子段和

 

第一种情况,两个区间无交集,

[x1,y1][x2,y2]即y1<x2,两段分离,即问左段的最大后缀+中间段的和+右段的最大前缀

第二种情况,两个区间有相交或包含的情况,

[x1, 【x2   y1]  , y2】 总体来说,都是这种情况,[x2,y1]是交集部分

①答案完全在[x2,y1]中,是[x2,y1]的区间最大值

②左端点向外扩展,即左端点在[x1,x2]中,右端点在[x2,y2]中,

询问[x1,x2]最大后缀+[x2,y2]最大前缀,注意减掉公共部分x2单点值

③右端点向外扩展,即右端点在[y1,y2]中,左端点在[x1,y1]中,

询问[x1,y1]最大后缀+[y1,y2]最大前缀,注意减掉公共部分y1单点值

④如果两个点都向外扩展,会在②③中各被算一次,因此,无需拎出来单独讨论

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int t,n,m,a[N],l1,l2,r1,r2;
struct node
{
	int lmx,rmx,mx,sum;
}e[N*4];
void pushup(node &fa,node &lson,node &rson)
{
	fa.sum=lson.sum+rson.sum;
	fa.lmx=max(lson.lmx,lson.sum+rson.lmx);
	fa.rmx=max(rson.rmx,rson.sum+lson.rmx);
	fa.mx=max(max(lson.mx,rson.mx),lson.rmx+rson.lmx);
}
void build(int p,int l,int r) 
{
	e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=0;
	if(l==r)
	{
		e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=a[l];
		return;
	}
	int mid=(l+r)/2;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(e[p],e[p<<1],e[p<<1|1]);
}
void update(int p,int l,int r,int x,int v)
{
	if(l==r)
	{
		e[p].lmx=e[p].rmx=e[p].mx=e[p].sum=v;
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid)update(p<<1,l,mid,x,v);
	else update(p<<1|1,mid+1,r,x,v);
	pushup(e[p],e[p<<1],e[p<<1|1]);
}
node ask(int p,int l,int r,int ql,int qr)
{
	if(ql>qr)return node{0,0,0,0};
	if(ql<=l&&r<=qr)return e[p];
	int mid=(l+r)/2;
	if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
	if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
	node ls=ask(p<<1,l,mid,ql,qr),rs=ask(p<<1|1,mid+1,r,ql,qr),ans;
	pushup(ans,ls,rs);
	return ans;
}
int query(int l1,int r1,int l2,int r2)
{
	//[l1, r1] [l2, r2]的分离 
	if(r1<l2)
	{
		int suf=ask(1,1,n,l1,r1).rmx;
		int mid=ask(1,1,n,r1+1,l2-1).sum;//ask中写入ql>qr的情形 
		int pre=ask(1,1,n,l2,r2).lmx;
		return suf+mid+pre;
	}
	//[l1  ,(l2  , r1]  r2)的嵌套与包含 
	int ans=ask(1,1,n,l2,r1).mx;//完全包含在[l2,r1]内 
	if(l1<l2)ans=max(ans,ask(1,1,n,l1,l2).rmx+ask(1,1,n,l2,r1).lmx-a[l2]);//l2端向左出头 l2端被计入两次 应减一次 注意如有修改a[]配套修改 
	if(r1<r2)ans=max(ans,ask(1,1,n,l1,r1).rmx+ask(1,1,n,r1,r2).lmx-a[r1]);//r1端向右出头 r1端被计入两次 
	return ans;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		scanf("%d",&a[i]);
		build(1,1,n);
		scanf("%d",&m);
		while(m--)
		{
			scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
			printf("%d\n",query(l1,r1,l2,r2));
		}
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值