SPOJ 2916 Can you answer these queries V(线段树-分类讨论)

题目链接:http://www.spoj.com/problems/GSS5/

题意:给出一个数列。每次查询最大子段和Sum[i,j],其中i和j满足x1<=i<=y1,x2<=j<=y2,x1<=x2,y1<=y2。

思路:线段树的节点[L,R]保存LMax,RMax,Max,sum,表示左起最大值、右起最大值、区间最大值、区间数字和。更新比较简单。下面说查询。另外设置三个函数,可以查询任意区间[L,R]的最大值,以L开始向右最多到R的最大值、以R开始向左最多到L的最大值,设其分别为Q(L,R),QL(L,R),QR(L,R)。查询分两种情况讨论(设输入数组为d,S[i]表示d的前i项和):

(1)x2>y1: ans=QR(x1,y1)+S[x2-1]-S[y1]+QL(x2,y2);

(2)x2<=y1,那么有x1<=x2<=y1<=y2,则答案为下面三种情况的最大值:

          a、x1<=i<=x2<=j<=y2,ans=QR(x1,x2)+QL(x2,y2)-d[x2];

          b、x2<=i<=j<=y1,ans=Q(x2,y1);

          c、x1<=i<=y1<=j<=y2,ans=QR(x1,y1)+QL(y1,y2)-d[y1]。





struct node
{
    int LMax,RMax,Max,sum;
    int L,R;
};




node a[N<<2];
int d[N],S[N];
int n;




void pushUp(int t)
{
    if(a[t].L==a[t].R) return;


    a[t].LMax=max(a[t*2].LMax,a[t*2].sum+a[t*2+1].LMax);
    a[t].RMax=max(a[t*2+1].RMax,a[t*2+1].sum+a[t*2].RMax);
    a[t].sum=a[t*2].sum+a[t*2+1].sum;
    a[t].Max=max3(a[t*2].Max,a[t*2+1].Max,a[t*2].RMax+a[t*2+1].LMax);
}






void build(int t,int L,int R)
{
    a[t].L=L;
    a[t].R=R;
    if(L==R)
    {
        a[t].sum=d[L];
        a[t].LMax=a[t].RMax=a[t].Max=d[L];
        return;
    }
    int mid=(L+R)>>1;
    build(t*2,L,mid);
    build(t*2+1,mid+1,R);
    pushUp(t);
}




int queryL(int t,int L,int R)
{
    if(a[t].L==L&&a[t].R==R) return a[t].LMax;


    int mid=(a[t].L+a[t].R)>>1;
    int ans;
    if(R<=mid) ans=queryL(t*2,L,R);
    else if(L>mid) ans=queryL(t*2+1,L,R);
    else
    {
        ans=max(queryL(t*2,L,mid),S[mid]-S[L-1]+queryL(t*2+1,mid+1,R));
    }
    return ans;
}


int queryR(int t,int L,int R)
{
    if(a[t].L==L&&a[t].R==R) return a[t].RMax;


    int mid=(a[t].L+a[t].R)>>1;
    int ans;
    if(R<=mid) ans=queryR(t*2,L,R);
    else if(L>mid) ans=queryR(t*2+1,L,R);
    else
    {
        ans=max(queryR(t*2,L,mid)+S[R]-S[mid],queryR(t*2+1,mid+1,R));
    }
    return ans;
}


int query(int t,int L,int R)
{
    if(a[t].L==L&&a[t].R==R) return a[t].Max;


    int mid=(a[t].L+a[t].R)>>1;
    int ans;
    if(R<=mid) ans=query(t*2,L,R);
    else if(L>mid) ans=query(t*2+1,L,R);
    else
    {
        int x=query(t*2,L,mid);
        int y=query(t*2+1,mid+1,R);
        ans=max(x,y);
        ans=max(ans,queryR(t*2,L,mid)+queryL(t*2+1,mid+1,R));
    }
    return ans;
}




int cal(int x1,int y1,int x2,int y2)
{
    int a,b,c;
    if(x2>y1)
    {
        a=queryR(1,x1,y1);
        b=queryL(1,x2,y2);
        return a+b+S[x2-1]-S[y1];
    }
    int ans;
    a=queryR(1,x1,x2);
    b=queryL(1,x2,y2);
    ans=a+b-d[x2];


    a=query(1,x2,y1);
    ans=max(ans,a);


    a=queryR(1,x1,y1);
    b=queryL(1,y1,y2);
    ans=max(ans,a+b-d[y1]);


    return ans;
}


int main()
{
    rush()
    {
        RD(n);
        int i;
        FOR1(i,n) RD(d[i]),S[i]=S[i-1]+d[i];
        build(1,1,n);
        int x1,y1,x2,y2;
        int m;
        RD(m);
        while(m--)
        {
            RD(x1,y1); RD(x2,y2);
            PR(cal(x1,y1,x2,y2));
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值