【codevs 3981】动态最大子段和 / 线段树

给定一个长度为 n n 的序列 ai,以及 q q 次询问,每次询问给定 l,r 两参数。
对于每次询问,求 al a l ar a r 之间的最大子段和,子段的意思是连续非空子区间。
更形式化地解释:对于每次询问给定的 l,r l , r ,
求一个整数 ans a n s ,使得存在整数 l,r l ′ , r ′ ,满足 llrr l ≤ l ′ ≤ r ′ ≤ r ri=lai=ans ∑ i = l ′ r ′ a i = a n s
其中, n,q200000 n , q ≤ 200000 ai a i int i n t 范围内但不保证答案在 int i n t 范围内。

看题就知道要数据结构啦,
我们直接考虑合并,对于某个区间,它的两个左右儿子区间怎样合并为这个区间?
首先对于每个节点,我们肯定要记录这个区间的最大子段和,记作 gss g s s 。但是如果直接把两个儿子的最大子段和取最大一定是错的,因为一个区间的最大子段可能会跨过左右区间的分界点,而不是单独分布在左区间或者右区间。
为了维护这种情况,我们得多记两个信息:
以该区间左/右端点为起点的最大子段和,分别记作 lgss,rgss l g s s , r g s s
如果能够维护,那么上述的另一种情况就也能被我们维护下来。我们只要把左区间的 rgss r g s s 加上右区间的 lgss l g s s ,就能得出跨分界点的最大子段和。
但我们在合并时要怎样维护 lgss l g s s rgss r g s s 呢?由于两个信息是对称的,我们拿 lgss l g s s 为例。
大区间的 lgss l g s s 一定等于左区间的 lgss l g s s 吗?不一定。因为可能跨过分界点。
如果跨过分界点,其就是左区间的所有元素和加上右区间的 lgss l g s s 。所以我们还得在维护一个区间和,这个非常好维护。
因此这就是合并了,放个代码:

void Merge(R node &fa,R node &s1,R node &s2)
{
    fa.gss=max(max(s1.gss,s2.gss),s1.rgss+s2.lgss);
    fa.lgss=max(s1.lgss,s1.sum+s2.lgss);
    fa.rgss=max(s2.rgss,s2.sum+s1.rgss);
    fa.sum=s1.sum+s2.sum;
}

(区间 s1 s 1 s2 s 2 合并为 fa f a 。)
理一理思路:

  • 每个节点记录以下四个信息: gss,lgss,rgss,sum g s s , l g s s , r g s s , s u m 。意义见上。
  • gss g s s 的维护
  • 情况一:该区间的最大子段完全落在左子区间或右子区间。 fa.gss=max(s1.gss,s2.gss) f a . g s s = m a x ( s 1. g s s , s 2. g s s )
  • 情况二·:该区间的最大子段跨过左右区间分界点。 fa.gss=s1.rgss+s2.lgss f a . g s s = s 1. r g s s + s 2. l g s s
  • lgss l g s s 的维护( rgss r g s s 同理)
  • 情况一:该区间的 lgss l g s s 对应的子段完全落在左子区间。 fa.lgss=s1.lgss f a . l g s s = s 1. l g s s
  • 情况二:该区间的 lgss l g s s 对应子段跨过分界点。 fa.lgss=s1.sum+s2.lgss f a . l g s s = s 1. s u m + s 2. l g s s
  • sum s u m 的维护: fa.sum=s1.sum+s2.sum f a . s u m = s 1. s u m + s 2. s u m

合并弄清楚了其它都不难。询问就把询问区间拆成线段树上的区间然后逐个合并即可。复杂度 O(n log n) O ( n   l o g   n )
完整代码:

#include <bits/stdc++.h>

using namespace std;

#define R register
#define Maxn 200005
#define LL long long
#define lson (now<<1)
#define rson (lson|1)
#define inf -9999999999999999

int n,q;
struct node
{
    LL gss,lgss,rgss,sum;
    node(){gss=lgss=rgss=sum=inf;return;}
}T[Maxn<<2];
void Merge(R node &fa,R node &s1,R node &s2)
{
    fa.gss=max(max(s1.gss,s2.gss),s1.rgss+s2.lgss);
    fa.lgss=max(s1.lgss,s1.sum+s2.lgss);
    fa.rgss=max(s2.rgss,s2.sum+s1.rgss);
    fa.sum=s1.sum+s2.sum;
}
void Build(R int now,R int l,R int r)
{
    if(l==r)
    {
        scanf("%lld",&T[now].gss);
        T[now].lgss=T[now].rgss=T[now].sum=T[now].gss;
        return ;
    }
    R int mid=(l+r)>>1;
    Build(lson,l,mid);Build(rson,mid+1,r);
    Merge(T[now],T[lson],T[rson]);
}
int ql,qr;
bool key;
node ans;
void Query(R int now,R int l,R int r)
{
    if(ql<=l && qr>=r) 
    {
        if(key) Merge(ans,ans,T[now]);
        else ans=T[now],key=1;
        return;
    }
    R int mid=(l+r)>>1;
    if(mid >= ql)Query(lson,l,mid);
    if(mid < qr)Query(rson,mid+1,r);
}
int main()
{
    scanf("%d",&n);
    Build(1,1,n);
    scanf("%d",&q);
    for (R int i=1;i<=q;++i)
    {
        scanf("%d %d",&ql,&qr);
        key=0;Query(1,1,n);
        printf("%lld\n",ans.gss);
    }
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值