UVa - 1400 "Ray, Pass me the dishes!" 线段树(最大子段和)

题目链接

题意:n个数的序列,m个询问,每次询问[i,j]内最大的子段和的区间。

思路:设一个区间起点为左边界的最大子段和为lsum,终点为右边界的最大子段和为rsum,整个区间和为sum。

区间[i,j]可以分成[i,mid],[mid+1,j],则[i,j]的最大子段和即为max([i,mid]的最大子段和,[mid+1,j]的最大子段和,[i,mid]的rsum+[mid+1,j]的lsum)。

那么[i,j]的lsum则为max([i,mid]的lsum,[i,mid]的sum+[mid+1,j]的lsum)。

[i,j]的rsum则为max([mid+1,j]的rsum,[mid+1,j]的sum+[i,mid]的rsum)。

#include<stdio.h>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
using namespace std;
#define p  -500000000000001
#define NUM 510000
#define ll long long
int n,m;
ll a[NUM];
struct ANS
{
    int st,en;
    ll sum;
    bool operator<(const ANS& x)const
    {
        if(sum!=x.sum)
            return sum<x.sum;
        if(st!=x.st)
            return st>x.st;
        return en>x.en;
    }
    ANS operator+(const ANS& x)const//合并两个子段和
    {
        ANS y;
        y.st=min(x.st,st);
        y.en=max(x.en,en);
        y.sum=x.sum+sum;
        return y;
    }
};
struct T
{
    int r,l,mid;
    ANS lw,rw,w,tsum;
}t[3*NUM];
void build(int x,int l,int r)
{
    t[x].l=l;
    t[x].r=r;
    t[x].mid=(l+r)>>1;
    if(l==r)
    {
        t[x].lw.st=t[x].lw.en=l;
        t[x].lw.sum=a[l];
        t[x].tsum=t[x].w=t[x].rw=t[x].lw;
        return ;
    }
    build(x<<1,l,t[x].mid);
    build(x<<1|1,t[x].mid+1,r);
    t[x].tsum=t[x<<1].tsum+t[x<<1|1].tsum;
    t[x].rw=max(t[x<<1|1].rw,t[x<<1].rw+t[x<<1|1].tsum);
    t[x].lw=max(t[x<<1].lw,t[x<<1|1].lw+t[x<<1].tsum);
    t[x].w=t[x<<1].rw+t[x<<1|1].lw;
    t[x].w=max(t[x].w,t[x<<1].w);
    t[x].w=max(t[x].w,t[x<<1|1].w);
}
ANS get(int x,int l,int r)//求整个区间和
{
    if(t[x].l==l&&t[x].r==r)
        return t[x].tsum;
    if(t[x].mid<l)
        return get(x<<1|1,l,r);
    if(t[x].mid>=r)
        return get(x<<1,l,r);
    return get(x<<1,l,t[x].mid)+get(x<<1|1,t[x].mid+1,r);
}
ANS solvel(int x,int l,int r)//求起点为l的最大子段和
{
    if(t[x].l==l&&t[x].lw.en<=r)
        return t[x].lw;
    if(t[x].mid<l)
        return solvel(x<<1|1,l,r);
    if(t[x].mid>=r)
        return solvel(x<<1,l,r);
    return max(get(x<<1,l,t[x].mid)+solvel(x<<1|1,t[x].mid+1,r),solvel(x<<1,l,t[x].mid));
}
ANS solver(int x,int l,int r)//求终点为r的最大子段和
{
    if(t[x].r==r&&t[x].rw.st>=l)
        return t[x].rw;
    if(t[x].mid<l)
        return solver(x<<1|1,l,r);
    if(t[x].mid>=r)
        return solver(x<<1,l,r);
    return max(get(x<<1|1,t[x].mid+1,r)+solver(x<<1,l,t[x].mid),solver(x<<1|1,t[x].mid+1,r));
}
ANS solve(int x,int l,int r)//求区间[l,r]的最大子段和
{
    if(t[x].w.st>=l&&t[x].w.en<=r)
        return t[x].w;
    if(t[x].mid<l)
        return solve(x<<1|1,l,r);
    if(t[x].mid>=r)
        return solve(x<<1,l,r);
    ANS ans=max(solve(x<<1,l,t[x].mid),solve(x<<1|1,t[x].mid+1,r));
    ans=max(ans,solver(x<<1,l,t[x].mid)+solvel(x<<1|1,t[x].mid+1,r));
    return ans;
}
int main()
{
    int l,r;
    int t=0;
    ANS x;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        ++t;
        for(int i=1;i<=n;++i)
            scanf("%lld",&a[i]);
        build(1,1,n);
        printf("Case %d:\n",t);
        while(m--)
        {
            scanf("%d%d",&l,&r);
            x=solve(1,l,r);
            printf("%d %d\n",x.st,x.en);
        }
    }
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值