• 线段树维护区间最大子段和


    线段树:我还是很强的


    简略讲解

    要用线段树维护区间,我们要明确:

    • 线段树存什么东西
    • 怎么合并
    • 如果有区间修改,怎么打标记

    对于区间最大子段和,我们可以记录四个值:以维护的区间左端点为起点的最大子段和,以维护的区间右端点为终点的最大子段和,在维护区间内的最大子段和 和维护区间所有元素的和

    合并的话稍微麻烦一些,看代码吧:

    inline void up(int p){
    	tree[p].sum=tree[ls].sum+tree[rs].sum; //维护区间总和
    	tree[p].ll=max(tree[ls].ll,tree[ls].sum+tree[rs].ll);
    //左端点的最大子段和可能为左儿子的左端点最大子段和,也可能为左儿子区间和 和右儿子左端点最大子段和拼起来的最大子段和
    	tree[p].lr=max(tree[rs].lr,tree[rs].sum+tree[ls].lr);
    //右端点最大子段和同理
    	tree[p].lm=max(tree[ls].lr+tree[rs].ll,max(tree[ls].lm,tree[rs].lm));
    //中间的最大子段和可能为左/右儿子中间的的最大子段和,也可能为左右儿子拼起来的和
    }
    

    然后我们的线段树那就可以维护最大子段和了。

    例题

    (1): SPOJ 1043

    GSS1 - Can you answer these queries I

    裸题,不解释,直接上代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ls p<<1
    #define rs p<<1|1
    #define mid ((l+r)>>1)
    #define inf 0x7ffffffffLL
    #define LL long long
    using namespace std;
    struct zzz{
    	LL ll,lr,lm,sum;
    }tree[200010<<2];
    int a[200010];
    inline void up(int p){
    	tree[p].sum=tree[ls].sum+tree[rs].sum;
    	tree[p].ll=max(tree[ls].ll,tree[ls].sum+tree[rs].ll);
    	tree[p].lr=max(tree[rs].lr,tree[rs].sum+tree[ls].lr);
    	tree[p].lm=max(tree[ls].lr+tree[rs].ll,max(tree[ls].lm,tree[rs].lm));
    }
    void build(int l,int r,int p){
    	if(l==r){
    		tree[p].sum=tree[p].ll=tree[p].lr=tree[p].lm=a[l];
    		return ;
    	}
    	build(l,mid,ls); build(mid+1,r,rs);
    	up(p);
    }
    zzz query(int l,int r,int p,int nl,int nr){
    	zzz a={-inf,-inf,-inf,-inf},b={-inf,-inf,-inf,-inf},ans={-inf,-inf,-inf,-inf};
    	if(l>=nl&&r<=nr) return tree[p];
    	if(nl<=mid) a=query(l,mid,ls,nl,nr);
    	if(nr>mid) b=query(mid+1,r,rs,nl,nr);
    	ans.sum=a.sum+b.sum;
    	ans.ll=max(a.ll,a.sum+b.ll);
    	ans.lr=max(b.lr,b.sum+a.lr);
    	ans.lm=max(a.lr+b.ll,max(a.lm,b.lm));
    	return ans;
    }
    int read(){
    	int k=0,f=1; char c=getchar();
    	for(;c<'0'||c>'9';c=getchar())
    	  if(c=='-') f=-1;
    	for(;c>='0'&&c<='9';c=getchar())
    	  k=(k<<3)+(k<<1)+c-48;
    	return k*f;
    }
    int main(){
    	int n=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	int m=read(); build(1,n,1);
    	for(int i=1;i<=m;i++){
    		int x=read(),y=read();
    		zzz k=query(1,n,1,x,y);
    		printf("%lld
    ",max(k.ll,max(k.lm,k.lr)));
    	}
    	return 0;
    }
    

    (2):SPOJ 1716

    GSS3 - Can you answer these queries III

    这题和上题相比,就多了一个单点修改,加一个修改函数就可以了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ls p<<1
    #define rs p<<1|1
    #define mid ((l+r)>>1)
    #define inf 0x7ffffffffLL
    #define LL long long
    using namespace std;
    struct zzz{
    	LL ll,lr,lm,sum;
    }tree[200010<<2];
    int a[200010];
    inline void up(int p){
    	tree[p].sum=tree[ls].sum+tree[rs].sum;
    	tree[p].ll=max(tree[ls].ll,tree[ls].sum+tree[rs].ll);
    	tree[p].lr=max(tree[rs].lr,tree[rs].sum+tree[ls].lr);
    	tree[p].lm=max(tree[ls].lr+tree[rs].ll,max(tree[ls].lm,tree[rs].lm));
    }
    void build(int l,int r,int p){
    	if(l==r){
    		tree[p].sum=tree[p].ll=tree[p].lr=tree[p].lm=a[l];
    		return ;
    	}
    	build(l,mid,ls); build(mid+1,r,rs);
    	up(p);
    }
    zzz query(int l,int r,int p,int nl,int nr){
    	zzz a={-inf,-inf,-inf,-inf},b={-inf,-inf,-inf,-inf},ans={-inf,-inf,-inf,-inf};
    	if(l>=nl&&r<=nr) return tree[p];
    	if(nl<=mid) a=query(l,mid,ls,nl,nr);
    	if(nr>mid) b=query(mid+1,r,rs,nl,nr);
    	ans.sum=a.sum+b.sum;
    	ans.ll=max(a.ll,a.sum+b.ll);
    	ans.lr=max(b.lr,b.sum+a.lr);
    	ans.lm=max(a.lr+b.ll,max(a.lm,b.lm));
    	return ans;
    }
    void update(int l,int r,int p,int nn,int k){
    	if(l==r){
    		tree[p].ll=tree[p].lm=tree[p].lr=tree[p].sum=k;
    		return ;
    	}
    	if(nn<=mid) update(l,mid,ls,nn,k);
    	if(nn>mid) update(mid+1,r,rs,nn,k);
    	up(p);
    }
    int read(){
    	int k=0,f=1; char c=getchar();
    	for(;c<'0'||c>'9';c=getchar())
    	  if(c=='-') f=-1;
    	for(;c>='0'&&c<='9';c=getchar())
    	  k=(k<<3)+(k<<1)+c-48;
    	return k*f;
    }
    int main(){
    	int n=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	int m=read(); build(1,n,1);
    	for(int i=1;i<=m;i++){
    		int f=read(),x=read(),y=read();
    		if(f==1){
    			zzz k=query(1,n,1,x,y);
    			printf("%lld
    ",max(k.ll,max(k.lm,k.lr)));
    		}
    		else  update(1,n,1,x,y);
    	}
    	return 0;
    }
    
  • 相关阅读:
    译文-浏览器下载图片的方式和时间点
    总结一下各种0.5px的线
    CSS3渐变效果工具
    [CSS]《CSS揭秘》第四章——视觉效果
    如何机制地回答浏览器兼容性问题
    如何更愉快地使用em —— 别说你懂CSS相对单位
    CSS学习(二):背景图片如何定位?
    React-简单通用的抛物线动画
    如何更愉快地使用rem —— 别说你懂CSS相对单位
    linuxC进程间通信的几种方式
  • 原文地址:https://www.cnblogs.com/morslin/p/11854787.html
Copyright © 2020-2023  润新知