hrbust 1684 最大连续和(区间合并)

题意:对于询问(a,b),需要找到

两个下标x和y,使得a<=x<=y<=b,并且Dx+Dx+1+....+Dy尽量大。如果有多组满足条件的x和y,x

分析:应尽量小。如果还有多解y也应尽量小。

构造一颗线段树节点保存区间最值,最大前缀和,最大后缀和,及他们对应的下标。

对于某一区间最值的更新:

1. 左区间的最值

2  右区间的最值

3  左区间的最大后缀和 右区间的最大前缀和

对于最大前缀和的更新:

1 左区间的最大前缀和

2 左区间的和 右区间的最大前缀和

下标在更新最值,最大前缀,最大后缀 时 一同更新。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define havemid int m=(l+r)>>1
#define left (rt<<1)
#define right (rt<<1|1)
#define LL long long
const int maxn=500100;
struct node {
    LL lsum,rsum,sum,mov;// 最大前缀和,最大后缀和,区间和,最大值
    int ll,rr,l,r;//最大前缀和端点, 最大后缀和端点,最大区间左右端点
}a[maxn<<2];
void pushup(int l,int r,int rt){
    havemid;
    a[rt].sum=a[left].sum+a[right].sum;  //区间和=左右+
    a[rt].lsum=a[left].lsum;             //父前缀和=左儿子前缀和    
    a[rt].rsum=a[right].rsum;            //右
    a[rt].ll=a[left].ll;                 //父前缀的端点=左儿子前缀的端点  
    a[rt].rr=a[right].rr;                //右
    if(a[left].sum+a[right].lsum>a[rt].lsum){//左儿子的和+右儿子的前缀>区间前缀的话
        a[rt].lsum=a[left].sum+a[right].lsum;//更新父前缀和and端点
        a[rt].ll=a[right].ll;
    }
    if(a[right].sum+a[left].rsum>a[rt].rsum){//右同
        a[rt].rsum=a[right].sum+a[left].rsum;
        a[rt].rr=a[left].rr;
    }
    int tmp;
    if(a[left].mov>=a[right].mov)tmp=left; 
    else tmp=right;                         //左右区间最值大的付给区间最值
    a[rt].mov=a[tmp].mov;a[rt].l=a[tmp].l;a[rt].r=a[tmp].r;
    LL tt=a[left].rsum+a[right].lsum;       //左的后缀和+右的前缀和>区间最值 并且端点小 更新   
    if(tt>a[rt].mov||(tt==a[rt].mov&&a[left].rr<a[rt].l)||
    (tt==a[rt].mov&&a[left].rr==a[rt].l&&a[right].ll<a[rt].r)){
        a[rt].mov=tt;a[rt].l=a[left].rr;a[rt].r=a[right].ll;
    }
}
void build(int l,int r,int rt){
    if(l==r){
        scanf("%lld",&a[rt].sum);
        a[rt].lsum=a[rt].rsum=a[rt].mov=a[rt].sum;//各种和都相等
        a[rt].ll=a[rt].rr=a[rt].l=a[rt].r=l;      //各种端点=l;  
        return ;
    }
    havemid;
    build(lson);
    build(rson);
    pushup(l,r,rt);
}
node query(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        return a[rt];
    }
    havemid;
    if(L>m)return query(L,R,rson);
    else if(R<=m)return query(L,R,lson);
    else {
        node t1=query(L,R,lson);
        node t2=query(L,R,rson);        
        node ret;
        if(t1.mov>=t2.mov)ret=t1;
        else ret=t2;
        ret.sum=t1.sum+t2.sum;
        LL tt=t1.rsum+t2.lsum;          //这么并可以保证在查询区间中
        if(tt>ret.mov||(tt==ret.mov&&t1.rr<ret.l)||
        (tt==ret.mov&&t1.rr==ret.l&&t2.ll<ret.r)){
            ret.mov=tt;ret.l=t1.rr;ret.r=t2.ll;
        }
        ret.lsum=t1.lsum;
        ret.rsum=t2.rsum;
        ret.ll=t1.ll;
        ret.rr=t2.rr;
        if(t1.sum+t2.lsum>ret.lsum){
            ret.lsum=t1.sum+t2.lsum;
            ret.ll=t2.ll;
        }
        if(t2.sum+t1.rsum>ret.rsum){
            ret.rsum=t2.sum+t1.rsum;
            ret.rr=t1.rr;
        }
        return ret;
    }
}
int main(){
    int T,n,m,a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        build(1,n,1);
        while(m--){
            scanf("%d%d",&a,&b);
            node t=query(a,b,1,n,1);
            printf("%d %d\n",t.l,t.r);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值