uva 1400 "Ray, Pass me the dishes!" 线段树

题意:

给定一个长度为n的序列fi,然后做m次询问,每次询问[l,r]输出这段区间内和最大的子区间。如果存在多个和最大,那取x较小的,如果还是相同,取y较小的。

题解:

被玩了,一天都在调试中。我用的是code blocks,long long没法用,输出的结果都不对,找了半天才发现。之后又出现将o,p,q定义为全局变量,递归的时候相互影响大哭

这道题是用线段树解得,只要看到区间询问基本上都想到的是线段树。。定义结构体,元素l和r表示最大和区间,sum表示区间的和,w表示最大和,pre表示这段区间的前缀最大和(即对于区间[L,R],那前缀就是[L,K](L<=K<=R)),suf是后缀,pr是前缀最大和的区间[L,K]中的K,su是后缀最大和区间[K,R]中的K。

区间分治,在递归的时候,最大和区间要么在左区间,要么在右区间,要么就是两个区间各一部分。若o是要求得区间,p,q分别是o的左右区间,那么o.w=max(p.w,q.w,p.suf+q.pre);而前缀最大和和后缀最大和也有类似的关系,o.pre=max(p.pre,p.sum+q.pre);。

注意:由于x和y要竟可能小,所以要注意比较的顺序。



代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
using namespace std;

#define LL long long
const int maxn=5e5+10;
struct node{
    LL pre,suf,w,sum;
    int l,r,pr,su;
}e[maxn*4];
LL f[maxn];
void build(int a,int b,int c)
{
    if(a==b)
    {
        e[c].sum=e[c].w=e[c].pre=e[c].suf=f[a];
        e[c].l=e[c].r=e[c].pr=e[c].su=a;
        return ;
    }

    int mid=(a+b)/2;
    build(a,mid,2*c);
    build(mid+1,b,2*c+1);
    e[c].sum=e[2*c].sum+e[2*c+1].sum;
    LL aa=e[2*c].w,bb=e[2*c+1].w,cc=e[2*c].suf+e[2*c+1].pre;
    if(aa>=bb&&aa>=cc){e[c].w=aa;e[c].l=e[2*c].l;e[c].r=e[2*c].r;}
    else if(cc>=bb){e[c].w=cc;e[c].l=e[2*c].su;e[c].r=e[2*c+1].pr;}
    else {e[c].w=bb;e[c].l=e[2*c+1].l;e[c].r=e[2*c+1].r;}
    if(e[2*c].pre>=e[2*c].sum+e[2*c+1].pre)
    {
        e[c].pre=e[2*c].pre;
        e[c].pr=e[2*c].pr;
        if(e[c].pr<0)printf("%d\n",e[c].pr);
    }
    else
    {
        e[c].pre=e[2*c].sum+e[2*c+1].pre;
        e[c].pr=e[2*c+1].pr;
        if(e[c].pr<0)printf("%d\n",e[c].pr);
    }
    if(e[2*c+1].suf>e[2*c].suf+e[2*c+1].sum)
    {
        e[c].suf=e[2*c+1].suf;
        e[c].su=e[2*c+1].su;
    }
    else
    {
        e[c].suf=e[2*c+1].sum+e[2*c].suf;
        e[c].su=e[2*c].su;
    }
}
node query(int c,int l,int r,int L,int R)
{
    //printf("%d\n",c);
    if(L==l&&R==r)return e[c];
    int mid=L+(R-L)/2;
    if(r<=mid)return query(2*c,l,r,L,mid);
    else if(l>mid)return query(2*c+1,l,r,mid+1,R);
    else
    {
        node p,q,o;
        p=query(2*c,l,mid,L,mid);

        q=query(2*c+1,mid+1,r,mid+1,R);
        o.sum=p.sum+q.sum;
        if(p.w>=q.w&&p.w>=p.suf+q.pre){o.w=p.w;o.l=p.l;o.r=p.r;}
        else if(p.suf+q.pre>=q.w){o.w=p.suf+q.pre;o.l=p.su;o.r=q.pr;}
        else {o.w=q.w;o.l=q.l;o.r=q.r;}
        if(p.pre>=p.sum+q.pre)
        {
            o.pre=p.pre;
            o.pr=p.pr;
        }
        else
        {
            o.pre=p.sum+q.pre;
            o.pr=q.pr;
        }
        if(q.suf>p.suf+q.sum)
        {
            o.suf=q.suf;
            o.su=q.su;
        }
        else
        {
            o.suf=q.sum+p.suf;
            o.su=p.su;
        }
        return o;
    }
}
int main()
{
    //freopen("D:\\in.txt","r",stdin);
    //freopen("C:\\Documents and Settings\\Administrator\\桌面\\false.txt","w",stdout);
    int n,m,tt=0;;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int i,j,k,x,y,l,r;
        for(i=1;i<=n;i++)
            scanf("%lld",&f[i]);
        build(1,n,1);
        node ans;
        printf("Case %d:\n",++tt);
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&l,&r);
            ans=query(1,l,r,1,n);
            //printf("%d %d\n",l,r);
            printf("%d %d\n",ans.l,ans.r);
        }
    }
    return 0;
}
/*
10 10
4 -5 -4 -1 4 -1 -3 1 -1 4
5 9
5 8
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值