UVa 1400 Ray, Pass me the dishes!(线段树:复杂段查询)

题意是给出一个序列以及下标a,b

让你输出满足a<=x<=y<=b的二元组(x, y)

使得该范围内的序列和在(a,b)中最大

这道题的思路是分情况合并子线段

其实这道题的复杂程度在于你选择的数据存储类型

我刚开始就是在线段树中记录太多东西了,写到后来写懵了

看了别人的代码才知道,记录序列的前缀和会省掉很多力气

而且在query中递归用PushUp的方法也很新奇

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 500005
#define LL long long
using namespace std;

LL a[MAXN];
LL sum[MAXN];

struct NODE {
    int prefix, suffix;//前缀、后缀
    int x, y;//该段的左右边界
    int subx, suby;//该段最大和对应的左右边界
};

NODE node[MAXN*2+1];

void PushUp(int l, int r, int rt) {
    NODE &tmp = node[rt];
    int L = rt<<1;
    int R = rt<<1|1;

    tmp.x = l;
    tmp.y = r;

    tmp.prefix = sum[node[L].prefix]-sum[l-1] >= sum[node[R].prefix]-sum[l-1] ? node[L].prefix : node[R].prefix;
    tmp.suffix = sum[r]-sum[node[L].suffix-1] >= sum[r]-sum[node[R].suffix-1] ? node[L].suffix : node[R].suffix;

    if(sum[node[L].suby]-sum[node[L].subx-1] >= sum[node[R].suby]-sum[node[R].subx-1]) {
        tmp.suby = node[L].suby;
        tmp.subx = node[L].subx;
    } else {
        tmp.suby = node[R].suby;
        tmp.subx = node[R].subx;
    }

    if(sum[node[R].prefix]-sum[node[L].suffix-1] > sum[tmp.suby]-sum[tmp.subx-1]) {
        tmp.subx = node[L].suffix;
        tmp.suby = node[R].prefix;
    } else if(sum[node[R].prefix]-sum[node[L].suffix-1] == sum[tmp.suby]-sum[tmp.subx-1])  {  
        if(tmp.subx > node[L].suffix||(tmp.subx == node[L].suffix && tmp.suby > node[R].prefix)) { 
            tmp.subx = node[L].suffix; 
            tmp.suby = node[R].prefix; 
        }  
    }  
        
}

void build(int l, int r, int rt) {
    NODE &tmp = node[rt];
    if(l == r) {
        tmp.x = l;
        tmp.y = l;
        tmp.prefix = l;
        tmp.suffix = l;
        tmp.subx = tmp.suby = l;
        return ;
    }
    int m = (l+r)>>1;
    build(l, m, rt<<1);
    build(m+1, r, rt<<1|1);

    PushUp(l, r, rt);

}

void query(int rt, int L, int R, int &ansx, int &ansy, int &prefix, int &suffix) {  

    int l = node[rt].x;
    int r = node[rt].y;  

    if(L<=l && R>=r)  {  
        ansx = node[rt].subx;  
        ansy = node[rt].suby;  
        prefix = node[rt].prefix;  
        suffix = node[rt].suffix;  
        return ;  
    }  

    int m = (l+r)>>1;  

    if(R <= m) query(rt<<1, L, R, ansx, ansy, prefix, suffix);  
    else if(L > m) query(rt<<1|1, L, R, ansx, ansy, prefix, suffix);  
    else {  
        int x1, x2, y1, y2, p1, p2, s1, s2;  
        query(rt<<1, L, m, x1, y1, p1, s1);  
        query(rt<<1|1, m+1, R, x2, y2, p2, s2);  
  
        prefix = sum[p1]-sum[L-1] >= sum[p2]-sum[L-1] ? p1 : p2 ;  
        suffix = sum[R]-sum[s1-1] >= sum[R]-sum[s2-1] ? s1 : s2 ;  
        if(sum[y1]-sum[x1-1] >= sum[y2]-sum[x2-1]) {  
            ansx = x1;  
            ansy = y1;  
        }  
        else {  
            ansx = x2;  
            ansy = y2;  
        }  
        LL LS = sum[m]-sum[s1-1];  
        LL RS = sum[p2]-sum[m];  
        if(LS+RS > sum[ansy]-sum[ansx-1] || (LS+RS == sum[ansy]-sum[ansx-1] && ansx > s1) || (LS+RS == sum[ansy]-sum[ansx-1] && ansx == s1 && ansy > p2))  
        {  
            ansx = s1;  
            ansy = p2;  
        }  
    }  
  
} 


int main(void) {
    int m, n, _, L, R;
    _ = 1;
    while(~scanf("%d%d", &n, &m)) {
        for(int i=1; i<=n; ++i) {
            scanf("%lld", &a[i]);
            sum[i] = sum[i-1]+a[i];
        }
        build(1, n, 1);
        printf("Case %d:\n", _++);
        int ansx, ansy, prefix, suffix;
        while(m--) {
            scanf("%d%d", &L, &R);
            query(1, L, R, ansx, ansy, prefix, suffix);
            printf("%d %d\n", ansx, ansy);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值