codevs 3981 动态最大子段和

3981 动态最大子段和

http://codevs.cn/problem/3981/

 题目等级 : 钻石 Diamond

题目描述 Description

题目还是简单一点好...

有n个数,a[1]到a[n]。

接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和。

子段的意思是连续非空区间。

输入描述 Input Description

第一行一个数n。

第二行n个数a[1]~a[n]。

第三行一个数q。

以下q行每行两个数l和r。

输出描述 Output Description

q行,每行一个数,表示a[l]到a[r]的最大子段和。

样例输入 Sample Input

7
2 3 -233 233 -23 -2 233
4
1 7
5 6
2 5
2 3

样例输出 Sample Output

441
-2
233
3

数据范围及提示 Data Size & Hint

对于50%的数据,q*n<=10000000。

对于100%的数据,1<=n<=200000,1<=q<=200000。

a[1]~a[n]在int范围内,但是答案可能超出int范围。

数据保证1<=l<=r<=n。

空间128M,时间1s。

线段树求GSS模板题

一、一段长的区间的 GSS 有三种情况:
>1 完全在左子区间
>2 完全在右子区间
>3 横跨左右区间

二、需维护的信息:

max 区间GSS  ——用来更新情况1、2

max_l 区间左端点开始的GSS——用来更新情况3

max_r 区间右端点开始的GSS——用来更新情况3

sum 区间和——用来更新max_l,max_r

三、建树

1、初始化:区间需维护的信息最初都赋为输入值

2、合并区间信息

max:3中情况中的最大值

max_l:左区间的max_l, 左区间的sum+右区间max_l  取大

max_r 同理

四、查询

情况1、2很简单

情况3的合并与上面的合并区间信息同理

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,opl,opr,cnt;
struct node
{
    int l,r;
    long long max,max_l,max_r,sum;
}e[4000001];
void query(int k,long long & ans,long long & ans_l,long long & ans_r)
{
    if(e[k].l>=opl&&e[k].r<=opr)
    {
        ans=e[k].max;
        ans_l=e[k].max_l;
        ans_r=e[k].max_r;
        return;
    }
    int mid=e[k].l+e[k].r>>1;
    if(opr<=mid) query(k<<1,ans,ans_l,ans_r);
    else if(opl>mid) query((k<<1)+1,ans,ans_l,ans_r);
    else
    {
        long long lch_max=0,lch_max_r=0,rch_max=0,rch_max_l=0,lch_max_l=0,rch_max_r=0;
        query(k<<1,lch_max,lch_max_l,lch_max_r);
        query((k<<1)+1,rch_max,rch_max_l,rch_max_r);
        ans=max(lch_max,rch_max);
        ans=max(ans,lch_max_r+rch_max_l);
        ans_l=max(lch_max_l,e[k<<1].sum+rch_max_l);
        ans_r=max(rch_max_r,e[(k<<1)+1].sum+lch_max_r);
    }
}
void unionn(int k)
{
    e[k].max=max(max(e[k<<1].max,e[(k<<1)+1].max),e[k<<1].max_r+e[(k<<1)+1].max_l);
    e[k].max_l=max(e[k<<1].max_l,e[k<<1].sum+e[(k<<1)+1].max_l);
    e[k].max_r=max(e[(k<<1)+1].max_r,e[(k<<1)+1].sum+e[k<<1].max_r);
    e[k].sum=e[k<<1].sum+e[(k<<1)+1].sum;
}
void build(int k,int l,int r)
{
    e[k].l=l,e[k].r=r;
    if(l==r)
    {
        cin>>e[k].max;
        e[k].max_l=e[k].max_r=e[k].sum=e[k].max;
        return;
    }
    int mid=l+r>>1;
    build(k<<1,l,mid);
    build((k<<1)+1,mid+1,r);
    unionn(k);
}
int main()
{
    scanf("%d",&n);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&opl,&opr);
        long long ans=0,ans_l=0,ans_r=0;
        query(1,ans,ans_l,ans_r);
        printf("%lld\n",ans);
    }
}

自己做的2个错误:

1、max_l的更新 : if 左区间的GSS是本身,max_l=max(左区间的max_l,左区间的sum+右区间的max_l)

else max_l=左区间的max_l

错因:例:左区间:5,8,-1,-1   右区间  1,1,1,1

错误方法的GSS=13   正确的GSS=15

2、在查询时ans,ans_l,ans_r的更新同max,max_l,max_r

因为max,max_l,max_r更新用到的左区间、右区间信息可以直接拿来用

而ans,ans_l,ans_r 更新用到的左区间、右区间信息不能直接拿来用,感觉比较棘手,实际只要在递归回溯时更新即可

时间: 2024-11-06 10:20:42

codevs 3981 动态最大子段和的相关文章

codevs 3981 动态最大子段和(线段树)

题目传送门:codevs 3981 动态最大子段和 题目描述 Description 题目还是简单一点好... 有n个数,a[1]到a[n]. 接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和. 子段的意思是连续非空区间. 输入描述 Input Description 第一行一个数n. 第二行n个数a[1]~a[n]. 第三行一个数q. 以下q行每行两个数l和r. 输出描述 Output Description q行,每行一个数,表示a[l]到a[r]的最大子段和. 样

CODEVS 3981(求最大子段和+线段树)

题目链接:http://codevs.cn/problem/3981/ 参考:https://blog.csdn.net/jokingcoder/article/details/81477253 一个区间的最大子段和有三种情况: 1.等于这个区间左儿子的最大子段和 2.等于这个区间右儿子的最大子段和 3.等于这个区间左儿子的后缀最大子段和+右儿子的前缀最大子段和 tree[k].sumax = max(max(tree[l].sumax,tree[r].sumax),tree[l].rmax +

3981 动态最大子段和

题目描述 Description 题目还是简单一点好... 有n个数,a[1]到a[n]. 接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和. 子段的意思是连续非空区间. 输入描述 Input Description 第一行一个数n. 第二行n个数a[1]~a[n]. 第三行一个数q. 以下q行每行两个数l和r. 输出描述 Output Description q行,每行一个数,表示a[l]到a[r]的最大子段和. 样例输入 Sample Input 72 3 -233

计划做题列表

估计是做不完.. emmmm  不用估计,是肯定 马上就滚回去学文化课了.... ....列表全都是数据结构... mdzz 我可能是傻了 1.数学  P1214 [USACO1.4]等差数列 Arithmetic Progressions https://www.luogu.org/problemnew/show/P1214 2.P4243 [JSOI2009]等差数列 线段树 https://www.luogu.org/problemnew/show/P4243 主席树 P3313 [SDO

浅谈线段树

 数据结构——线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:o(n)枚举. 动态修改最起码不能用静态的前缀和做了. 好,我再修改题目: C.给出n个数,n<=1000000,

线段树讲解(转)

转自   http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html  数据结构——线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:

转自大佬的线段树

结构——线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:o(n)枚举. 动态修改最起码不能用静态的前缀和做了. 好,我再修改题目: C.给出n个数,n<=1000000,和m个

[POI2009]Lyz

Description 初始时滑冰俱乐部有1到n号的溜冰鞋各k双.已知x号脚的人可以穿x到x+d的溜冰鞋. 有m次操作,每次包含两个数ri,xi代表来了xi个ri号脚的人.xi为负,则代表走了这么多人. 对于每次操作,输出溜冰鞋是否足够. Input n m k d ( 1≤n≤200,000 , 1≤m≤500,000 , 1≤k≤10^9 , 0≤d≤n ) ri xi ( 1≤i≤m, 1≤ri≤n-d , |xi|≤10^9 ) Output 对于每个操作,输出一行,TAK表示够 NIE

SP1716 GSS3 - Can you answer these queries III 线段树

题目传送门:SP1043 GSS1 - Can you answer these queries I 更好的阅读体验 动态维护子段和最大值 前置知识 静态维护子段和最大值:SP1043 GSS1 - Can you answer these queries I 题解传送 题解: 提供结构体指针线段树写法: 设\(l\)为区间左端点, \(r\)为区间右端点: \(ls\)为以\(l\)为左端点的最大子段和, \(rs\)为以\(r\)为右端点的最大子段和; \(sum\)为区间和, \(val\