【线段树】带修最大子段和模板

算是线段树里面很裸的一类题了,但是考场上忘记了怎么写qwq。
考试的题是环上的,补题的时候发现很熟悉,就想起来这一类模型之前刚学线段树老师讲过呀qwq(没有认真听讲),但是之前的板子已经被埋没在学校的老oj里面了,就还是重新写了一个模板:
小白逛公园

首先,线段树要维护这样四个域:
sum:该区间的和
max:该区间上的最大子段和
maxl:为必须包含左端点的最大子段和(相当于最大前缀)
maxr:为必须包含右端点的最大子段和(相当于最大后缀)

maxl和maxr的使用使得更新非常地方便,详情见下:

修改时

1、max:左右儿子的max,左儿子的maxr+右儿子的maxl,取最大值。
2、maxl:左儿子的maxl与左儿子sum和右儿子maxl和的最大值。
3、maxr:右儿子的maxr与右儿子sum和左儿子maxr和的最大值。
4、sum:左右儿子sum之和。

查询时
1、如果查询区间覆盖这一节点,将该节点信息返回。
2、如果只与一个儿子有交集,就返回在那个儿子中查找到的信息。
3、如果与两个儿子都有交集,就先分别计算出两个儿子的信息,然后按修改的方式将两个信息合并,然后返回。
4、最后返回的值即为答案。

代码:

//带修最大子段和模板 
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 500005
#define ll long long
#define INF 0x3f3f3f3f
struct node{
	int l,r;
	int sum,ms/*maxsum*/,ml,mr/*maxl,maxr*/;
}tree[N*4];
int n,m,a[N];
inline int rd()
{
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
//-----
void PushUp(int i)
{
	tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
	tree[i].ml=max(tree[i<<1].sum+tree[i<<1|1].ml,tree[i<<1].ml);
	tree[i].mr=max(tree[i<<1|1].sum+tree[i<<1].mr,tree[i<<1|1].mr);
	tree[i].ms=max(max(tree[i<<1].ms,tree[i<<1|1].ms),tree[i<<1].mr+tree[i<<1|1].ml);
}
void build(int i,int l,int r)
{
	tree[i].l=l,tree[i].r=r;
	if(l==r)
	{
		tree[i].sum=tree[i].ml=tree[i].mr=tree[i].ms=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	PushUp(i);
}
void update(int i,int pos,int val)
{
	if(tree[i].l==tree[i].r)
	{
		tree[i].ms=tree[i].ml=tree[i].mr=tree[i].sum=val;
		return ;
	}
	int mid=(tree[i].l+tree[i].r)>>1;
	if(pos<=mid)
		update(i<<1,pos,val);
	else update(i<<1|1,pos,val);
	PushUp(i);
}
node query(int i,int l,int r)
{
	if(l<=tree[i].l&&tree[i].r<=r)
		return tree[i];
	int mid=(tree[i].l+tree[i].r)>>1;
	if(r<=mid) return query(i<<1,l,r);
	else if(l>mid) return query(i<<1|1,l,r);
	else
	{
		node x=query(i<<1,l,r),y=query(i<<1|1,l,r),res;
		//合并答案 
		res.sum=x.sum+y.sum;
		res.ml=max(x.sum+y.ml,x.ml);
		res.mr=max(y.sum+x.mr,y.mr);
		res.ms=max(max(x.ms,y.ms),x.mr+y.ml);
		return res;
	}
}
//-----
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=n;i++)
		a[i]=rd();
	build(1,1,n);
	while(m--)
	{
		int opt=rd();
		if(opt==1)
		{
			int x=rd(),y=rd(),tmp;
			if(x>y) tmp=x,x=y,y=tmp;
			printf("%d\n",query(1,x,y).ms);
		}
		else if(opt==2)
		{
			int x=rd(),y=rd();
			update(1,x,y);
		}
	}
	return 0;
}

下面这种实现是在结构体里面去掉了l和r,带到函数里面去, 据说 可以省空间。

//带修最大子段和模板 
#include
#include
#include
#include
#include
#include
using namespace std;
#define N 500005
#define ll long long
#define INF 0x3f3f3f3f
struct node{
	int sum,ms/*maxsum*/,ml,mr/*maxl,maxr*/;
}tree[N*4];
int n,m,a[N];
inline int rd()
{
	int f=1,x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return f*x;
}
//-----
void PushUp(int i)
{
	tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
	tree[i].ml=max(tree[i<<1].sum+tree[i<<1|1].ml,tree[i<<1].ml);
	tree[i].mr=max(tree[i<<1|1].sum+tree[i<<1].mr,tree[i<<1|1].mr);
	tree[i].ms=max(max(tree[i<<1].ms,tree[i<<1|1].ms),tree[i<<1].mr+tree[i<<1|1].ml);
}
void build(int i,int l,int r)
{
	if(l==r)
	{
		tree[i].sum=tree[i].ml=tree[i].mr=tree[i].ms=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	PushUp(i);
}
void update(int i,int pos,int val,int l,int r)
{
	if(l==r)
	{
		tree[i].ms=tree[i].ml=tree[i].mr=tree[i].sum=val;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)
		update(i<<1,pos,val,l,mid);
	else update(i<<1|1,pos,val,mid+1,r);
	PushUp(i);
}
node query(int i,int li,int ri,int l,int r)
{
	if(l<=li&&ri<=r)
		return tree[i];
	int mid=(li+ri)>>1;
	if(r<=mid) return query(i<<1,li,mid,l,r);
	else if(l>mid) return query(i<<1|1,mid+1,ri,l,r);
	else
	{
		node x=query(i<<1,li,mid,l,r),y=query(i<<1|1,mid+1,ri,l,r),res;
		//合并答案 
		res.sum=x.sum+y.sum;
		res.ml=max(x.sum+y.ml,x.ml);
		res.mr=max(y.sum+x.mr,y.mr);
		res.ms=max(max(x.ms,y.ms),x.mr+y.ml);
		return res;
	}
}
//-----
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=n;i++)
		a[i]=rd();
	build(1,1,n);
	while(m--)
	{
		int opt=rd();
		if(opt==1)
		{
			int x=rd(),y=rd(),tmp;
			if(x>y) tmp=x,x=y,y=tmp;
			printf("%d\n",query(1,1,n,x,y).ms);
		}
		else if(opt==2)
		{
			int x=rd(),y=rd();
			update(1,x,y,1,n);
		}
	}
	return 0;
}

你可能感兴趣的:(数据结构-线段树)