SPOJ GSS1 最大区间和

次元传送门,扶稳坐好

题意:
给定一个长度为n的序列,可能存在负数
之后是m次询问,每次给出x和y
请你在[x..y]中找到一个子区间使其区间和最大

分析:
线段树练习题,懂?

一开始用自己的方法写了一棵枝繁叶茂的树,改了改就TLE了
后来借鉴别人的模板,才算是学会了这种写法
貌似实现起来复杂度差别还是挺大的,虽然不明觉厉

代码实现(正解)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define Max(i,j) i>j?i:j 
//据说这样能快点,比algorithm库中的函数更牛逼一点,至少看起来确实如此
#define MAX(i,j,k) Max(i,max(j,k))
//由于Sec值得传递会对三个量取MAX,所以这样写一句会方便很多

const int N=500002; 
//看到留言中有人说N给的范围不对,也没多想,反正大点不会MLE,看看内存限制就知道了

struct Node{
    int Left,Right,Sum,Pre,Suf,Sec;
    //分别存储左边界,右边界,区间和,最大前缀,最大后缀,最大子区间和
}Tree[N*4];

int V[N];//原数组,用来直接存储Input数据并建树

void Build(int s,int t,int n){//植树函数,s为区间左界,t为区间右界,n为当前结点下标
    int mid=(s+t)>>1;//位运算优化,求区间中界
    Tree[n].Left=s;
    Tree[n].Right=t;//对当前结点的左右界赋值
    if(s==t){//如果当前结点为叶子结点
        Tree[n].Sec=Tree[n].Sum=Tree[n].Pre=Tree[n].Suf=V[s];
        //那么各项值都为序列中的第n个数,没商量
        return;
    }
    //进入下面就代表当前结点不是叶子结点
    Build(s,mid,2*n);//对左儿子建树
    Build(mid+1,t,2*n+1);//对有儿子建树
    //左儿子结点定位2n,右儿子为2n+1
    Tree[n].Sum=Tree[2*n].Sum+Tree[2*n+1].Sum;//Sum由两个儿子相加合成
    Tree[n].Pre=Max(Tree[2*n].Pre,Tree[2*n].Sum+Tree[2*n+1].Pre);//Pre只可能是左儿子的最大前缀或左儿子所有+右儿子最大前缀,取个MAX就好
    Tree[n].Suf=Max(Tree[2*n+1].Suf,Tree[2*n].Suf+Tree[2*n+1].Sum);
    Tree[n].Sec=MAX(Tree[2*n].Sec,Tree[2*n+1].Sec,Tree[2*n].Suf+Tree[2*n+1].Pre);//最大子区间和可以用分治的思想考虑,只能是左儿子的最大子区间和、右儿子的最大子区间和、左儿子的最大后缀+右儿子的最大前缀这三者中的一者,取个MAX就好
}

Node GetAns(int s,int t,int p){//用来得到某个区间的性质,这个函数会将其Sec、Pre、Suf打包返回
    if(Tree[p].Left==s&&Tree[p].Right==t)
        return Tree[p];
    int mid=(Tree[p].Left+Tree[p].Right)>>1;
    if(mid>=t)
        return GetAns(s,t,2*p);
    if(mid<s)
        return GetAns(s,t,2*p+1);
    Node LeftAns=GetAns(s,mid,2*p);
    Node RightAns=GetAns(mid+1,t,2*p+1);
    Node Ans;
    Ans.Sec=MAX(LeftAns.Sec,RightAns.Sec,LeftAns.Suf+RightAns.Pre);
    Ans.Pre=Max(LeftAns.Pre,LeftAns.Sum+RightAns.Pre);
    Ans.Suf=Max(RightAns.Suf,LeftAns.Suf+RightAns.Sum);
    return Ans;
}//其转移方程类比植树函数中的就好,注意Mid和植树中的Mid写法不太一样哦,因为要保证他们一样

int main(void){//主函数不解释
    int n,m,x,y;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",V+i);
    Build(1,n,1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&x,&y),printf("%d\n",GetAns(x,y,1).Sec);
    return 0;
}//By YOUSIKI

代码实现(TLE版本)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

#define MAX(i,j,k) max(i,max(j,k))
#define INF 0x3f3f3f3f

const int N=500005;

struct Node{

    int Left,Right,Sum,MaxElement,MaxPrefix,MaxSuffix,MaxSection;
    Node *LeftSon,*RightSon;

    Node(){
        Left=Right=Sum=MaxElement=MaxPrefix=MaxSuffix=MaxSection=0;
        LeftSon=RightSon=NULL;
    }

    void Clear(void){
        if(LeftSon!=NULL)
            LeftSon->Clear();
        if(RightSon!=NULL)
            RightSon->Clear();
        Left=Right=Sum=MaxElement=MaxPrefix=MaxSuffix=MaxSection=0;
        LeftSon=RightSon=NULL;
    }

    void BuildTree(const int Vector[],const int L,const int R){
        if(L>=R)    return;
        Left=L,Right=R;
        if(L==R-1){
            Sum=Vector[L];
            MaxElement=Sum;
            MaxPrefix=max(MaxPrefix,Sum);
            MaxSuffix=max(MaxSuffix,Sum);
            MaxSection=max(MaxSection,Sum);
        }
        else{
            const int Mid=(L+R)>>1;
            LeftSon=new Node,RightSon=new Node;
            LeftSon->BuildTree(Vector,L,min(Mid,R));
            RightSon->BuildTree(Vector,max(Mid,L),R);
            Sum=LeftSon->Sum+RightSon->Sum;
            MaxElement=max(LeftSon->MaxElement,RightSon->MaxElement);
            MaxPrefix=max(LeftSon->MaxPrefix,LeftSon->Sum+RightSon->MaxPrefix);
            MaxSuffix=max(RightSon->MaxSuffix,RightSon->Sum+LeftSon->MaxSuffix);
            MaxSection=MAX(LeftSon->MaxSection,RightSon->MaxSection,LeftSon->MaxSuffix+RightSon->MaxPrefix);
        }
    }

    int GetMaxPrefix(const int L,const int R){
        if(L>=R)    return 0;
        if(L==Left&&R==Right)
            return MaxPrefix;
        const int Mid=(Left+Right)>>1;
        return max(LeftSon->GetMaxPrefix(L,min(R,Mid)),LeftSon->GetSum(L,min(Mid,R))+RightSon->GetMaxPrefix(max(Mid,L),R));
    }

    int GetMaxSuffix(const int L,const int R){
        if(L>=R)    return 0;
        if(L==Left&&R==Right)
            return MaxSuffix;
        const int Mid=(Left+Right)>>1;
        return max(RightSon->GetMaxSuffix(max(Mid,L),R),RightSon->GetSum(max(Mid,L),R)+LeftSon->GetMaxSuffix(L,min(Mid,R)));
    }

    int GetMaxSection(const int L,const int R){
        if(L>=R)    return 0;
        if(L==Left&&R==Right)
            return MaxSection;
        const int Mid=(Left+Right)>>1;
        return MAX(LeftSon->GetMaxSection(L,min(Mid,R)),RightSon->GetMaxSection(max(Mid,L),R),LeftSon->GetMaxSuffix(L,min(Mid,R))+RightSon->GetMaxPrefix(max(Mid,L),R));
    }

    int GetSum(const int L,const int R){
        if(L>=R)    return 0;
        if(L==Left&&R==Right)
            return Sum;
        const int Mid=(Left+Right)>>1;
        return LeftSon->GetSum(L,min(Mid,R))+RightSon->GetSum(max(Mid,L),R);
    } 

    int GetMaxElement(const int L,const int R){
        if(L>=R)    return -INF;
        if(L==Left&&R==Right)
            return MaxElement;
        const int Mid=(Left+Right)>>1;
        return max(LeftSon->GetMaxElement(L,min(Mid,R)),RightSon->GetMaxElement(max(Mid,L),R));
    }

}Root;

int V[N],n,m;

int main(){//SPOJ GSS1 (TLE)
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&V[i]);
    Root.BuildTree(V,1,n+1);
    scanf("%d",&m);
    for(int i=0,x,y;i<m;i++){
        scanf("%d%d",&x,&y);
        int ans=Root.GetMaxSection(x,y+1);
        printf("%d\n",ans?ans:Root.GetMaxElement(x,y+1));
    }
    return 0;
}//By YOUSIKI

注意:
前者中区间下标是左闭右闭的,而后者是左闭右开的

By YOUSIKI

你可能感兴趣的:(SPOJ GSS1 最大区间和)