SPOJ 1043 Can you answer these queries I(GSS1 线段树)

http://www.spoj.pl/problems/GSS1/

题意:给一个序列,求区间的最大连续子序列和。

思路:维护三个值区间的最大连续和,左端连续的最大和。右端连续的最大连续和。

查询的时候先看左右两个区间的最大连续和,再看两区间交界处的最大连续和。(参考:http://blog.csdn.net/acm_cxlove/article/details/7982444

View Code
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int maxn = 50005;
int sum[maxn],bs[maxn];
struct nd{
    int l,r,lx,rx,mx;
    int all(){return sum[r]-sum[l-1];}//整个区间和
}as[maxn*4];
void pushUp(int rt)
{
    //max(左孩子的左端最大连续和,左区间总和+右区间的左端最大连续和)==>构成当前区间的左端最大连续和
    as[rt].lx = max(as[rt<<1].lx,as[rt<<1].all()+as[rt<<1|1].lx);
    //max(右孩子 的右端最大连续和,右区间的总和+左区间的右端最大连续和)==>构成当前区间的右端最大连续和
    as[rt].rx = max(as[rt<<1|1].rx,as[rt<<1|1].all()+as[rt<<1].rx);
    //max(左右孩子的最大连续区间和,左孩子的右端最大连续和+右孩子的左端最大连续和)==>构成当前区间的最大连续和
    as[rt].mx = max(max(as[rt<<1].mx,as[rt<<1|1].mx),as[rt<<1].rx+as[rt<<1|1].lx);
}
void build(int rt,int l,int r)
{
    int md = (l + r) >> 1;
    as[rt].l = l;
    as[rt].r = r;
    if(l==r){
        as[rt].lx = as[rt].rx = as[rt].mx = bs[l];
        return;
    }
    build(rt<<1,l,md);
    build(rt<<1|1,md+1,r);
    pushUp(rt);
}
//注意:区间[l,r],l一定是所查询左端最大连续和中的值之一
int queryL(int rt,int l,int r)
{
    int md = (as[rt].l+as[rt].r) >> 1;
    if(as[rt].l==l && r==as[rt].r)
        return as[rt].lx;
    if(r<=md)return queryL(rt<<1,l,r);
    if(l>md) return queryL(rt<<1|1,l,r);
    //max(左孩子的左端最大连续和,max(左区间的总和,左区间的总和+右区间的左端最大连续和))
    return max(queryL(rt<<1,l,md),max(sum[md]-sum[l-1],sum[md]-sum[l-1]+queryL(rt<<1|1,md+1,r)));
}
//注意:区间[l,r],r一定是所查询右端最大连续和中的值之一
int queryR(int rt,int l,int r)
{
    int md = (as[rt].l + as[rt].r) >> 1;
    if(as[rt].l==l && r==as[rt].r)
        return as[rt].rx;
    if(r<=md)return queryR(rt<<1,l,r);
    if(l>md) return queryR(rt<<1|1,l,r);
    //max(右孩子的右端最大连续和,max(右区间的总和,右区间的总和+左区间的右端最大连续和))
    return max(queryR(rt<<1|1,md+1,r),max(sum[r]-sum[md],sum[r]-sum[md]+queryR(rt<<1,l,md)));
}

int query(int rt,int l,int r)
{
    int md = (as[rt].l + as[rt].r) >> 1;
    if(as[rt].l==l&&as[rt].r==r)
        return as[rt].mx;
    if(r<=md)return query(rt<<1,l,r);
    if(l>md) return query(rt<<1|1,l,r);
    //max(左右区间的最大连续和,左区间的右端最大连续和+右区间的左端最大连续和)
    else return max(max(query(rt<<1,l,md),query(rt<<1|1,md+1,r)),queryR(rt<<1,l,md)+queryL(rt<<1|1,md+1,r));
}

int main()
{
    int n,q,i,l,r;
    while(scanf("%d",&n)==1){
        for(i = 1; i <= n; ++ i){
            scanf("%d",bs+i);
            sum[i] = sum[i-1] + bs[i];
        }
        build(1,1,n);
        scanf("%d",&q);
        while(q--){
            scanf("%d %d",&l,&r);
            printf("%d\n",query(1,l,r));
        }
    }
    return 0;
}
/*
10
1 2 3 4 5 6 7 8 9 10
10
*/

posted on 2012-09-17 14:04  aigoruan  阅读(160)  评论(0)    收藏  举报

导航