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

线段树:我还是很强的


简略讲解

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

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

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

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

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\n",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\n",max(k.ll,max(k.lm,k.lr)));
        }
        else  update(1,n,1,x,y);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wxl-Ezio/p/9507899.html

时间: 2024-10-30 02:28:54

线段树维护区间最大子段和的相关文章

线段树维护区间最大子段和 枚举 HDU6638

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6638 题意:在一个二维坐标系上给你n(n<=2000)个点,点带有一个价值w(有正有负),点的坐标都在(-1e9,1e9)的范围之间,可任意用一个平行于坐标轴的矩形框住一片区域,求这片区域框住的点的价值和 分析:点的坐标范围太大,离散化应能想到.离散化后可以考虑枚举左边界,枚举左边界后按照横坐标的依次加点(以一列一列为单位),用线段树维护一列的最大子段和,每移动到新的一列,继续加点时,等价于向原先的

最敏捷的机器人(线段树维护区间最值)

题面: Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了--机器人们都想知道谁是最敏捷的,于是它们进行了如下一个比赛.首先,他们面前会有一排共n个数,它们比赛看谁能最先把每连续k个数中最大和最小值写下来,当然,这些机器人运算速度都很,它们比赛的是谁写得快.但是Wind也想知道答案,你能帮助他吗? Input: 每组测试数据 第1行为n,k(1<=k<=n<=100000) 第2行共n个数,为数字序列,所有数字均在int范围内. Output: 共n-k+1行 第

线段树维护区间开方/除法

今天考试考了一些神仙数据结构 T1 线段树维护区间加,区间开方,区间和 (数据范围:5e5) T2 线段树维护区间加,区间除,区间和,区间最值 对于这些题目,就像是之前考的区间与,区间或一样,除法,开方的操作会让各个数字之间越来越相近,最后变成一串一串连续的数字都是一样的,所以对于这一部分的操作我们一定程度上使用暴力,而如果一段都相等就相当于直接进行区间剪发的操作 那么我们来看如何判断区间一段都相等,那我们只需要维护区间的最值,最小值==最大值就完全相等了 然后.....代码.....被 \(y

【GDKOI2016Day1T1-魔卡少女】【拆位】线段树维护区间内所有连续子区间的异或和

题意:给出N个数,M个操作.操作有修改和询问两种,每次修改将一个数改成另一个数,每次询问一个区间的所有连续子区间的异或和.n,m<=100000,ai<=1000 题解: 当年(其实也就是今年)做不出来的题..D1T1啊... 因为ai<=1000,我们可以拆位处理.拆成10个二进制位,每位开1棵线段树. 对于每个节点,维护: d:这段区间的异或和 L[0],L[1]:子区间一定从左端点开始,异或和为0,1的子区间分别有多少个 R[0],R[1]:子区间一定从右端点开始,异或和为0,1的

HDU 2795 Billboard 【线段树维护区间最大值&amp;&amp;查询变形】

任意门:http://acm.hdu.edu.cn/showproblem.php?pid=2795 Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 28743    Accepted Submission(s): 11651 Problem Description At the entrance to the un

线段树维护区间合并——cf1285E

感觉自己的解法又是歪的 代码写的很乱..要先找出一开始有多少段,然后计算删掉每条线段的贡献,求个最大值就可以 删每条线段的贡献可以用线段树区间合并来做 ps:正解其实很简单..扫描一下就可以了 /* 先把所有线段覆盖到线段树上 然后对每一个线段[L,R],查询区间[L,R]有多少值>1的段即可 */ #include<bits/stdc++.h> using namespace std; #define N 800005 int n,x[N],L[N],R[N],tot; #define

Can you answer these queries I SPOJ - GSS1 (线段树维护区间连续最大值/最大连续子段和)

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. Inp

SP1043 GSS1 - Can you answer these queries I(线段树,区间最大子段和(静态))

题目描述 给出了序列A[1],A[2],…,A[N]. (a[i]≤15007,1≤N≤50000).查询定义如下: 查询(x,y)=max{a[i]+a[i+1]+...+a[j]:x≤i≤j≤y}. 给定M个查询,程序必须输出这些查询的结果. 输入输出格式 输入格式: 输入文件的第一行包含整数N. 在第二行,N个数字跟随. 第三行包含整数M. M行跟在后面,其中第1行包含两个数字xi和yi. 输出格式: 您的程序应该输出M查询的结果,每一行一个查询. 思路: 我们做这道题首先应该想的,是两个

hdu 4553 约会安排 (两个线段树维护区间)

include include include include include include include include define ll long long define FOR(i,l,r) for(int i = l ; i <= r ;++i ) define inf 1<<30 define EPS (1e-9) define lson(p) (p<<1) define rson(p) (p<<1|1) using namespace std;