Uva 1400 "Ray, Pass me the dishes!" (线段树 区间合并)

这道题是训练指南上的一道例题,今天照着书上的标程敲了一遍,学了一下他的线段树写法。由于求的是一个区间,所以用pair来做结点,非常方便。在求区间和的时候先预处理一个前缀和,这样就可以用O(1)的复杂度求任意区间的和。这道题的难点在于区间合并。

一个区间上和最大的子区间等于这个区间的两个子区间的最大子区间的最大值和 左区间的最大后缀加上右区间的最大前缀三者中最大的和。也就是以下式子:

PII i1 = query_rmax(lson);
        PII i2 = query_lmax(rson);
        PII i3 = better(query(lson),query(rson));
        return better(make_pair(i1.first,i2.second),i3);

而最大后缀和最大前缀都是可以递归处理出来的(虽然比较繁琐。。。基本照搬书上代码)

把AC代码粘出来(实在太弱了我。。)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
using namespace std;
const int maxn = 500005;
typedef pair<int ,int> PII;
int qL,qR;
LL sum[maxn];
LL getsum(int L,int R){
    return sum[R]-sum[L-1];
}

LL getsum(PII p){
    return getsum(p.first,p.second);
}
PII better(PII a,PII b){
    if(getsum(a)!=getsum(b))return getsum(a)>getsum(b)?a:b;
    return a<b?a:b;
}

struct xdTree{
    int lmax[maxn<<2];
    int rmax[maxn<<2];
    PII mmax[maxn<<2];

    void build(int l,int r,int rt){
        if(l == r){
            lmax[rt] = rmax[rt] = l;
            mmax[rt] = make_pair(l,l);
        }else{
            int m = (l+r)>>1;
            build(lson);
            build(rson);

            LL v1 = getsum(l,lmax[rt<<1]);
            LL v2 = getsum(l,lmax[rt<<1|1]);
            if(v1==v2)lmax[rt] = min(lmax[rt<<1],lmax[rt<<1|1]);
            else lmax[rt] = v1>v2?lmax[rt<<1]:lmax[rt<<1|1];

            v1 = getsum(rmax[rt<<1],r);
            v2 = getsum(rmax[rt<<1|1],r);
            if(v1 == v2)rmax[rt] = min(rmax[rt<<1],rmax[rt<<1|1]);
            else rmax[rt] = v1>v2?rmax[rt<<1]:rmax[rt<<1|1];

            mmax[rt] = better(mmax[rt<<1],mmax[rt<<1|1]);
            mmax[rt] = better(mmax[rt],make_pair(rmax[rt<<1],lmax[rt<<1|1]));
        }
    }

    PII query_lmax(int l,int r,int rt){
        if(lmax[rt]<=qR) return make_pair(l,lmax[rt]);
        int m = (l+r)>>1;
        if(qR<=m)return query_lmax(lson);
        PII i = query_lmax(rson);
        i.first = l;
        return better(i,make_pair(l,lmax[rt<<1]));
    }
    PII query_rmax(int l,int r,int rt){
        if(rmax[rt]>=qL) return make_pair(rmax[rt],r);
        int m = (l+r)>>1;
        if(qL>m)return query_rmax(rson);
        PII i = query_rmax(lson);
        i.second = r;
        return better(i,make_pair(rmax[rt<<1|1],r));
    }
    PII query(int l,int r,int rt){
        if(qL<=l&&r<=qR)return mmax[rt];
        int m = (l+r)>>1;
        if(qR<=m)return query(lson);
        if(qL>m)return query(rson);
        PII i1 = query_rmax(lson);
        PII i2 = query_lmax(rson);
        PII i3 = better(query(lson),query(rson));
        return better(make_pair(i1.first,i2.second),i3);
    }
}tree;

int main(){
    int cas = 1,n,q,temp;
    while(scanf("%d%d",&n,&q)==2){
        sum[0] = 0;
        for(int i=1;i<=n;i++){
            scanf("%d",&temp);
            sum[i] = sum[i-1]+temp;
        }
        tree.build(1,n,1);
        printf("Case %d:\n",cas++);
        while(q--){
            scanf("%d%d",&qL,&qR);
            PII ans = tree.query(1,n,1);
            printf("%d %d\n",ans.first,ans.second);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值