关闭

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

184人阅读 评论(0) 收藏 举报
分类:

题目链接: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
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:15999次
    • 积分:1238
    • 等级:
    • 排名:千里之外
    • 原创:107篇
    • 转载:4篇
    • 译文:0篇
    • 评论:0条
    文章分类