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


    题目大意:

    nn个数,qq次操作

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

    思路:

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

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

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

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

    这里写图片描述

    情况一中的tree[x].lmaxtree[x].lmaxtree[x×2].lmaxtree[x\times 2].lmax,而情况二中的tree[x].lmaxtree[x].lmaxtree[x×2].sum+tree[x×2+1].lmaxtree[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].lmax=max(tree[x\times 2].lmax,tree[x\times 2].sum+tree[x\times 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].sumtree[x].sum就可以了。很容易发现,tree[x].sumtree[x].sum只有三种可能:

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

    这里写图片描述

    所以就有:
    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)

    那么我们也就维护好了最大子段和,再在每次更改中再重新更改就可以了,时间复杂度还是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;
    }
    
  • 相关阅读:
    java类加载过程
    进程和线程
    Java对象的创建
    java异常—检查异常(checked exception)和未检查异常(unchecked exception)
    try...catch...finally语句块
    Mysql数据库索引
    java-生产者消费者模式
    解压字符串
    Java-break,continue,return用法
    归并排序-Java
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998643.html
Copyright © 2020-2023  润新知