UVa 1400 "Ray, Pass me the dishes!"(线段树)

题目链接:http://acm.hust.edu.cn/vjudge/problem/36216


大意:给出n个数m个询问,对于每个询问(a,b),求(a,b)区间内的最大连续和区间(x,y),若有多组解,应使二元组(x,y)尽可能小。


思路:

(以下区间均为闭区间)设m为区间(a,b)的中点,则(a,b)内的最大连续和区间有三种情况,一是区间落在(a,m)内,二是落在(m+1,b)内,三是区间(a,m)的最大后缀和区间加上区间(m+1,b)的最大前缀区间。所以在建树时,要维护三个值,设该结点所对应区间为(a,b),则一为最大和区间(x,y),二为其最大前缀区间(a,x),三为最大后缀区间(x,b)。


而一个区间的最大前缀区间为(a,左子树最大前缀区间的右端点)或(a,右子树最大前缀和区间的右端点),最大后缀区间与其相似。最大和区间要么完全分布在左子树或右子树,要么是左右子树均有(此时则为左子树的最大后缀区间加上右子树的 最大前缀区间)


在查询时同样也是三种情况,但是若待查询区间落在结点区间的左右两区间内,此时不能简单认为答案就是左子树的最大后缀加上右子树的最大前缀,因为这俩区间并起来可能超出了待查询区间的范围,所以此时要特殊讨论一下。这里简单举个例子,假设结点所对应区间为(A,B),其中点为M,待查询区间为(a,b),且a<=M<b,我们要从(A,M)中找一个最大后缀(L,M),其中L>=a。首先,(A,M)的最大后缀区间在建树时已经算出来了,假设为(aa,M),那么如果aa>=x,则答案就是(aa,M)。如果不满足,设(A,M)的中点为m,那么最大后缀区间的左端点可能落在(A,m)内,也可能是(m+1,M)内,分情况讨论即可。详见代码。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
using namespace std;

typedef long long ll;
typedef pair<int,int> Interval;
const int maxn = 5e5 + 5;
const int maxnode = maxn<<1;
ll pre_sum[maxn];

ll sum(int l, int r)
{
    return pre_sum[r] - pre_sum[l-1];
}

ll sum(Interval p)
{
    return sum(p.first, p.second);
}

Interval better(Interval a, Interval b) //在和相同的情况下选择更优区间
{
    if (sum(a) != sum(b)) return sum(a) > sum(b) ? a : b;
    return a < b ? a : b;
}

struct IntervalTree
{
    int max_prefix[maxnode]; //最大前缀
    int max_suffix[maxnode]; //最大后缀
    Interval max_sub[maxnode]; //最大和

    void clear()
    {
        memset(max_prefix, 0, sizeof max_prefix);
        memset(max_suffix, 0, sizeof max_suffix);
        memset(max_sub, 0, sizeof max_sub);
    }

    void build(int l, int r, int rt)
    {
        if (l == r) {
            max_sub[rt] = Interval(l,r);
            max_prefix[rt] = l;
            max_suffix[rt] = r;
            return;
        }
        int m = (l + r) >> 1;
        build(lson);
        build(rson);
        //最大前缀区间
        max_prefix[rt] = better(Interval(l,max_prefix[lc]), Interval(l,max_prefix[rc])).second;
        //最大后缀区间
        max_suffix[rt] = better(Interval(max_suffix[lc],r), Interval(max_suffix[rc],r)).first;
        //最大和区间
        max_sub[rt] = better(max_sub[lc], max_sub[rc]);
        max_sub[rt] = better(max_sub[rt], Interval(max_suffix[lc], max_prefix[rc]));
    }

    Interval query_suffix(int qL, int qR, int l, int r, int rt)
    {
        if (max_suffix[rt] >= qL) return Interval(max_suffix[rt],qR);
        int m = (l + r) >> 1;
        if (qL > m) return query_suffix(qL,qR,rson);
        Interval A = query_suffix(qL,m,lson);
        A.second = qR;
        return better(A, Interval(max_suffix[rc],qR));
    }

    Interval query_prefix(int qL, int qR, int l, int r, int rt)
    {
        if (max_prefix[rt] <= qR) return Interval(qL,max_prefix[rt]);
        int m = (l + r) >> 1;
        if (qR <= m) return query_prefix(qL,qR,lson);
        Interval A = query_prefix(m+1,qR,rson);
        A.first = qL;
        return better(A, Interval(qL,max_prefix[lc]));
    }

    Interval query(int qL, int qR, int l, int r, int rt)
    {
        if (qL <= l && r <= qR) return max_sub[rt];
        int m = (l + r) >> 1;
        if (qR <= m) return query(qL,qR,lson);
        if (qL > m) return query(qL,qR,rson);
        Interval A = better(query(qL,m,lson), query(m+1,qR,rson));
        Interval B = query_suffix(qL,m,lson);
        Interval C = query_prefix(m+1,qR,rson);
        return better(A, Interval(B.first, C.second));
    }
};

IntervalTree Sum;

int main()
{
    int n,m,kase = 0;
    while(~scanf("%d%d",&n,&m)) {
        for (int i = 1; i <= n; i++) {
            int a;
            scanf("%d",&a);
            pre_sum[i] = pre_sum[i-1] + a;
        }
        printf("Case %d:\n", ++kase);
        Sum.clear();
        Sum.build(1,n,1);
        while(m--) {
            int a,b;
            scanf("%d%d",&a,&b);
            Interval ans = Sum.query(a,b,1,n,1);
            printf("%d %d\n",ans.first,ans.second);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值