[OC]题解 !(简单)的线段树

!(简单)的线段树 - 问题 - 我的编程之路


题目描述

给你一个序列A[1],A[2],...,A[n].(|A[i]| <= 15007, 1 <= N <= 50,000).

M(1 <= M <= 500,000) 次询问,每次询问 Query(x, y) = Max{A[i] + A[i+1] +...+ A[j]; x <= i <= j <= y}.

输入

第一行输入一个数N。

第二行输入N个数A[1],A[2],...,A[n].

第三行输入一个数M

以下M行,每行输入x , y.

输出

M行,每行输出查询的答案。

样例输入 Copy

3

-1 2 3

1

1 2

样例输出 Copy

2

提示

题目大意:求区间最大连续子段和。

解题思路:线段树需要维护的有四个值,sum,maxSum, lsum, rsum。 sum是区间和,maxSum是区间最大连续子段和,lsum是区间最大前缀和,rsum是区间最大后缀和。

sum的维护很常规,

lsum:有两种情况:

1.该区间内的lsum是ta左儿子的lsum

2.该区间内的lsum是左儿子的sum+右儿子的lsum

同理,rsum:有两种情况:

1.该区间内的rsum是ta右儿子的rsum

2.该区间内的rsum是右儿子的sum+左儿子的rsum

而maxSum有三种情况:

1.该区间内的maxSum是左儿子的maxSum

2.该区间内的maxSum是右儿子的maxSum

  1. 该区间内的maxSum是左儿子的rsum+右儿子的lsum


以下为题解

#include<bits/stdc++.h>
#define MAXN 500005
#define lson num<1,s,mid
#define rson num<<1|1,mid+1,e
using namespace std;
int tree[MAXN<<2];
int lef[MAXN<<2];
int rig[MAXN<<2];
int X[MAXN]={0};
int n,cnt;
inline void pushup(int num,int s,int e){
    int mid=(s+e)>>1;
    lef[num]=max(lef[num<<1],X[mid]-X[s-1]+lef[num<<1|1]);
    rig[num]=max(rig[num<<1|1],X[e]-X[mid]+rig[num<<1]);
    tree[num]=max(lef[num<<1|1]+rig[num<<1],max(tree[num<<1],tree[num<<1|1]));
}void build(int num,int s,int e){
    if(s==e){
        scanf("%d",&tree[num]);
        lef[num]=rig[num]=tree[num];
        X[cnt]=X[cnt-1]+tree[num];
        cnt++;
        return;
    }
    int mid=(s+e)>>1;
    build(num<<1,s,mid);
    build(num<<1|1,mid+1,e);
    pushup(num,s,e);
}int query(int num,int s,int e,int l,int r,int flag,int &ans){
    int mid=(s+e)>>1;
    if(s>=l&&e<=r){
        ans=max(ans,tree[num]);
        return flag == -1 ? lef[num] : rig[num];
    }if(mid>=r){
        return query(num<<1,s,mid,l,r,-1,ans);
    }else if(mid<l){
        return query(num<<1|1,mid+1,e,l,r,1,ans);
    }else{
        int ln,rn;
        ln=query(num<<1,s,mid,l,r,1,ans);
        rn=query(num<<1|1,mid+1,e,l,r,-1,ans);
        ans=max(ans,ln+rn);
        if(flag==-1) return max(lef[num<<1],X[mid]-X[s-1]+rn);
        else return max(rig[num<<1|1],X[e]-X[mid]+ln);
    }
}int main(){
    while(scanf("%d",&n)!=EOF){
        cnt=1;
        X[0]=0;
        build(1,1,n);
        int m;
        scanf("%d",&m);
        while(m--){
            int aa,bb;
            scanf("%d%d",&aa,&bb);
            int ans=X[aa]-X[aa-1];
            query(1,1,n,aa,bb,-1,ans);
            printf("%d\n",ans);
        }
    }
}

最后别忘了三怜!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值