题目大意:

nnn个数,qqq次操作

  • 0 x y0\ x\ y0 x ya[x]a[x]a[x]修改为yyy
  • 1 l r1\ l\ r1 l r询问区间[l,r][l, r][l,r]的最大子段和

思路:

操作0很明显是可以用线段树维护的。但是问题是操作而应该如何进行操作1。
原来的treetreetree只包含两个元素l,rl,rl,r,现在在里面再加入四个元素。

  • tree[x].sumtree[x].sumtree[x].sum,表示区间xxx的每个点的权值之和
  • tree[x].maxtree[x].maxtree[x].max,表示区间xxx的最大子段和
  • tree[x].lmaxtree[x].lmaxtree[x].lmax,表示区间xxx的含点tree[x].ltree[x].ltree[x].l的最大子段和
  • tree[x].rmaxtree[x].rmaxtree[x].rmax,表示区间xxx的含点tree[x].rtree[x].rtree[x].r的最大子段和

那么很明显,tree[x].sumtree[x].sumtree[x].sum其实就是左右儿子的sumsumsum之和。那么就有
tree[x].sum=tree[x×2].sum+tree[x×2+1].sumtree[x].sum=tree[x\times 2].sum+tree[x\times 2+1].sumtree[x].sum=tree[x×2].sum+tree[x×2+1].sum
然后tree[x].lmaxtree[x].lmaxtree[x].lmax必须含有tree[x].ltree[x].ltree[x].l,那么它就有以下几种可能:

  1. 完全位于它的左儿子中
  2. 占据了全部的左儿子,并且有一部分在右儿子中,而且这部分必须在右儿子的最左边

【SP1716 GSS3】Can you answer these queries III【线段树】_线段树

情况一中的tree[x].lmaxtree[x].lmaxtree[x].lmaxtree[x×2].lmaxtree[x\times 2].lmaxtree[x×2].lmax,而情况二中的tree[x].lmaxtree[x].lmaxtree[x].lmaxtree[x×2].sum+tree[x×2+1].lmaxtree[x\times 2].sum+tree[x\times 2+1].lmaxtree[x×2].sum+tree[x×2+1].lmax(右儿子的全部和左儿子的一部分)
所以就得到了:
tree[x].lmax=max(tree[x×2].lmax,tree[x×2].sum+tree[x×2+1].lmax)tree[x].lmax=max(tree[x\times 2].lmax,tree[x\times 2].sum+tree[x\times 2+1].lmax)tree[x].lmax=max(tree[x×2].lmax,tree[x×2].sum+tree[x×2+1].lmax)
那么同理可得:
tree[x].rmax=max(tree[x×2+1].rmax,tree[x×2+1].sum+tree[x×2].rmax)tree[x].rmax=max(tree[x\times 2+1].rmax,tree[x\times 2+1].sum+tree[x\times 2].rmax)tree[x].rmax=max(tree[x×2+1].rmax,tree[x×2+1].sum+tree[x×2].rmax)

那么只要求出tree[x].sumtree[x].sumtree[x].sum就可以了。很容易发现,tree[x].sumtree[x].sumtree[x].sum只有三种可能:

  1. 完全是右儿子的最大子段和
  2. 完全是左儿子的最大子段和
  3. 既有一部分在左儿子中,又有一部分在右儿子中

【SP1716 GSS3】Can you answer these queries III【线段树】_最大子段和_02

所以就有:
tree[x].max=max(tree[x×2].max,tree[x×2+1].max,tree[x×2].rmax+tree[x×2+1].lmax)tree[x].max=max(tree[x\times2].max,tree[x\times2+1].max,tree[x\times2].rmax+tree[x\times2+1].lmax)tree[x].max=max(tree[x×2].max,tree[x×2+1].max,tree[x×2].rmax+tree[x×2+1].lmax)

那么我们也就维护好了最大子段和,再在每次更改中再重新更改就可以了,时间复杂度还是O(nlogn)O(nlogn)O(nlogn)


代码:

#include <cstdio>
#include <algorithm>
#define N 500010
using namespace std;

int a[N];
int n,m,w,x,y,z;

struct node  //线段树
{
	int l,r,sum,max,lmax,rmax;
}tree[N*4];

int maxx(int x,int y,int z)
{
	return max(x,max(y,z));
}

void make(int x)  //建树
{
	if (tree[x].l==tree[x].r)   //叶子节点
	{
		tree[x].sum=a[tree[x].l];
		tree[x].lmax=a[tree[x].l];
		tree[x].rmax=a[tree[x].l];
		tree[x].max=a[tree[x].l];  //只有一个可能,它本身
		return;
	}
	int mid=(tree[x].l+tree[x].r)/2;
	tree[x*2].l=tree[x].l;
	tree[x*2].r=mid;
	tree[x*2+1].l=mid+1;
	tree[x*2+1].r=tree[x].r;  //左右儿子的边界
	make(x*2);
	make(x*2+1);
	tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
	tree[x].lmax=max(tree[x*2].lmax,tree[x*2].sum+tree[x*2+1].lmax);
	tree[x].rmax=max(tree[x*2+1].rmax,tree[x*2+1].sum+tree[x*2].rmax);
	tree[x].max=maxx(tree[x*2].max,tree[x*2+1].max,tree[x*2].rmax+tree[x*2+1].lmax);  //求出每个点的所有值
}  

node find(int x,int l,int r)  //查找
{  
	if (tree[x].l==l&&tree[x].r==r) return tree[x];  //找到
	if (tree[x].l==tree[x].r) return (node){0,0,0,0,0,0};  //不成立
	int mid=(tree[x].l+tree[x].r)/2;
	if (r<=mid) return (node)find(x*2,l,r);
	if (l>mid) return (node)find(x*2+1,l,r); 
	node a=find(x*2,l,mid),b=find(x*2+1,mid+1,r),c;
	c.sum=a.sum+b.sum;
	c.lmax=max(a.lmax,a.sum+b.lmax);
	c.rmax=max(b.rmax,b.sum+a.rmax);
	c.max=maxx(a.max,b.max,a.rmax+b.lmax);  //求答案
	return c;
}

void add(int x,int y,int k)  //修改
{
	if (tree[x].l==y&&tree[x].r==y)  //找到
	{
		tree[x].sum=k;
		tree[x].lmax=k;
		tree[x].rmax=k;
		tree[x].max=k;  //重新更新
		return;
	}
	if (tree[x].l==tree[x].r) return;
	int mid=(tree[x].l+tree[x].r)/2;
	if (y<=mid)
	{
		add(x*2,y,k);
		tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
		tree[x].lmax=max(tree[x*2].lmax,tree[x*2].sum+tree[x*2+1].lmax);
		tree[x].rmax=max(tree[x*2+1].rmax,tree[x*2+1].sum+tree[x*2].rmax);
		tree[x].max=maxx(tree[x*2].max,tree[x*2+1].max,tree[x*2].rmax+tree[x*2+1].lmax);  //重新更新
	}
	else
	{
		add(x*2+1,y,k);
		tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
		tree[x].lmax=max(tree[x*2].lmax,tree[x*2].sum+tree[x*2+1].lmax);
		tree[x].rmax=max(tree[x*2+1].rmax,tree[x*2+1].sum+tree[x*2].rmax);
		tree[x].max=maxx(tree[x*2].max,tree[x*2+1].max,tree[x*2].rmax+tree[x*2+1].lmax);  //重新更新
	}
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	 scanf("%d",&a[i]);
	tree[1].l=1;
	tree[1].r=n;
	make(1);	
	scanf("%d",&m);
	while (m--)
	{
		scanf("%d%d%d",&z,&x,&y);
		if (z)
		{
			if (x>y) swap(x,y);
			find(1,x,y);			
			printf("%d\n",find(1,x,y).max);
		}
		else
		{
			add(1,x,y);
		}
	}
	return 0;
}