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

版权声明:All rights reserved. https://blog.csdn.net/qq_42814118/article/details/81517140

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

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

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

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

合并弄清楚了其它都不难。询问就把询问区间拆成线段树上的区间然后逐个合并即可。复杂度 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;
} 

猜你喜欢

转载自blog.csdn.net/qq_42814118/article/details/81517140