SPOJ GSS1 Can you answer these queries I (线段树求区间最大连续和)

来源:互联网 发布:破解软件网盘 编辑:程序博客网 时间:2024/05/16 09:11

题目大意

给定一个数列,并给出一些询问的区间,求出询问区间内的最大连续区间和。

解题思路

直接上线段树,但是区间合并的时候遇到了一些困难。

首先我们考虑,想要达到 logn 级别的访问时间,对于线段树的每个节点中必须保存有该节点所表示区间内的最大连续区间和 mmax 。

然后我们考虑区间合并时最大值的取值情况:

1、等于左子结点中的区间最大值。
2、等于右子结点中的区间最大值。
3、最大连续区间横跨左子区间和右子区间。

在情况(1,2)和情况 3 取最大值的时候会遇到一些问题, 所以对于每个结点我们需要引入新的变量来存储更多的信息:

1、sum 表示该结点区间和。
2、suml 表示该结点包含最左端元素的最大连续区间和。
3、sumr 表示该结点包含最右端元素的最大连续区间和。

因此可得 mmax 的递推方程为

p[root].mmax=max(max(p[root<<1].mmax,p[root<<1|1].mmax),(p[root<<1].sumr+p[root<<1|1].suml));(root为当前的根节点)

suml 和 sumr 的递推方程为

p[root].suml=max(p[root<<1].suml,p[root<<1].sum+p[root<<1|1].suml);
p[root].sumr=max(p[root<<1|1].sumr,p[root<<1].sumr+p[root<<1|1].sum);

查询过程:

定义查询区间为 [ l , r ] , mid 为结点区间 [ left , right ] 中点
1、若查询区间位于 mid 左,则返回左子区间的查询结果。
2、若查询区间位于 mid 右,则返回右子区间的查询结果。
3、横跨左右子区间,则分别查询 [ l , mid ] 和 [ mid+1 , r ] 后按照上述规则合并。

附上代码

#include <cstdio>#include <cstring>#include <algorithm>#define MAX 50000+50using namespace std;int num[MAX];struct node{    int left,right;    int mmax,sum,suml,sumr;}p[MAX<<2];int a[MAX];void build(int id,int l,int r){    p[id].left=l;    p[id].right=r;    if(l==r)    {        p[id].sum=a[l];        p[id].mmax=a[l];        p[id].suml=a[l];        p[id].sumr=a[l];    }    else    {        int mid=(l+r)/2;        build(2*id,l,mid);        build(2*id+1,mid+1,r);        p[id].sum=p[2*id].sum+p[2*id+1].sum;        p[id].mmax=max(max(p[2*id].mmax,p[2*id+1].mmax),(p[2*id].sumr+p[2*id+1].suml));        p[id].suml=max(p[2*id].suml,p[2*id].sum+p[2*id+1].suml);        p[id].sumr=max(p[2*id+1].sumr,p[2*id].sumr+p[2*id+1].sum);    }}node query_sum(int id,int l,int r){    if(p[id].left==l&&p[id].right==r)        return p[id];    else    {        node tmp,k1,k2;        int mid=(p[id].left+p[id].right)/2;        if(r<=mid)            return query_sum(2*id,l,r);        if(l>mid)            return query_sum(2*id+1,l,r);        k1=query_sum(2*id,l,mid);        k2=query_sum(2*id+1,mid+1,r);        tmp.sum=k1.sum+k2.sum;        tmp.suml=max(k1.suml,k1.sum+k2.suml);        tmp.sumr=max(k2.sumr,k1.sumr+k2.sum);        tmp.mmax=max(max(k1.mmax,k2.mmax),k1.sumr+k2.suml);        return tmp;    }}int main(){    int n,q;    while(~scanf("%d",&n)){        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        build(1,1,n);        scanf("%d",&q);        while(q--){            int left,right;            scanf("%d%d",&left,&right);            node ans=query_sum(1,left,right);            printf("%d\n",ans.mmax);        }    }    return 0;}
0 0