UVALive 3938 - "Ray, Pass me the dishes!"(线段树区间合并)

题目链接 https://cn.vjudge.net/problem/UVALive-3938

【题意】
给定一个长度为n的整数序列D,你的任务是对m个询问作出回答。对于询问(a,b),需要找到两个下标x,y,使得a<=x<=y<=b,并且D(x)+D(x+1)+…+D(y)的值最大,如果有多解,要让x尽可能小,如果x确定后还有多解,要让y尽可能小。

【输入格式】
多组输入。每组数据第一行为两个整数n,m(1<=n,m<=500000)代表区间D的长度和查询次数,第二行包含n个整数,代表序列D的值,这些整数的绝对值小于1e9。接下来的m行,每行包含两个整数a,b,代表查询的左右区间。

【输出格式】
对每组数据,输出数据组数编号,然后每组查询输出一行x,y

【思路】
线段树区间合并问题,对序列D构造一颗线段树,维护4个值,pre,suf,ansx,ansy,分别代表最大前缀和下标,最大后缀和下标,还有要查询的答案的左右下标。这几个值都是可以递推求解的,在知道了左右子结点的所有信息后,当前结点的pre要么是左结点的pre,要么是右结点的pre,取前缀和更大的那一个,后缀和下标suf同理。然后就是要查询的答案ansx,ansy,首先它可以取左右结点的ansx,ansy里对应序列里和更大的一组,还要考虑的就是左结点的最大后缀和suf,和右结点的最大前缀和pre,这两段拼接起来形成的区间也有可能是最优解,需要进行比较,同时在答案相同的情况下保证x尽量小,y尽量小。查询时候的方法类似,把查询的区间分成左右区间递归查找答案。

#include
using namespace std;
typedef long long ll;
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]

const int maxn=500050;

struct Tree{
    int left,right;
    int ansx,ansy,pre,suf;
};

int n,m;
ll s[maxn];
Tree tree[maxn<<2];

void pushup(int id){
    if(s[lson.pre]-s[node.left-1]>=s[rson.pre]-s[node.left-1]) node.pre=lson.pre;
    else node.pre=rson.pre;

    if(s[node.right]-s[lson.suf-1]>=s[node.right]-s[rson.suf-1]) node.suf=lson.suf;
    else node.suf=rson.suf;

    if(s[lson.ansy]-s[lson.ansx-1]>=s[rson.ansy]-s[rson.ansx-1]){
        node.ansx=lson.ansx;
        node.ansy=lson.ansy;
    }
    else{
        node.ansx=rson.ansx;
        node.ansy=rson.ansy;
    }
    if(s[rson.pre]-s[lson.suf-1]==s[node.ansy]-s[node.ansx-1]){
        if(lson.suf.ansx){
            node.ansx=lson.suf;
            node.ansy=rson.pre;
        }
        else if(lson.suf==node.ansx && rson.pre.ansy){
            node.ansy=rson.pre;
        }
    }
    else if(s[rson.pre]-s[lson.suf-1]>s[node.ansy]-s[node.ansx-1]){
        node.ansx=lson.suf;
        node.ansy=rson.pre;
    }
}

void build(int id,int le,int ri){
    node.left=le;
    node.right=ri;
    if(le==ri){
        node.ansx=node.ansy=node.pre=node.suf=le;
        return;
    }
    int mid=(le+ri)>>1;
    build(id<<1,le,mid);
    build(id<<1|1,mid+1,ri);
    pushup(id);
}

Tree query(int id,int x,int y){
    if(x<=node.ansx && node.ansy<=y) return node;
    if(y<=lson.right) return query(id<<1,x,y);
    if(x>=rson.left) return query(id<<1|1,x,y);

    int mid=(node.left+node.right)>>1;
    Tree a=query(id<<1,x,mid);
    Tree b=query(id<<1|1,mid+1,y);
    Tree ans;

    if(s[a.pre]-s[x-1]>=s[b.pre]-s[x-1]) ans.pre=a.pre;
    else ans.pre=b.pre;

    if(s[y]-s[a.suf-1]>=s[y]-s[b.suf-1]) ans.suf=a.suf;
    else ans.suf=b.suf;

    if(s[a.ansy]-s[a.ansx-1]>=s[b.ansy]-s[b.ansx-1]){
        ans.ansx=a.ansx;
        ans.ansy=a.ansy;
    }
    else{
        ans.ansx=b.ansx;
        ans.ansy=b.ansy;
    }
    if(s[b.pre]-s[a.suf-1]==s[ans.ansy]-s[ans.ansx-1]){
        if(a.suf.ansx){
            ans.ansx=a.suf;
            ans.ansy=b.pre;
        }
        else if(a.suf==ans.ansx && b.pre.ansy){
            ans.ansy=b.pre;
        }
    }
    else if(s[b.pre]-s[a.suf-1]>s[ans.ansy]-s[ans.ansx-1]){
        ans.ansx=a.suf;
        ans.ansy=b.pre;
    }
    return ans;
}

int main(){
    int kase=0;
    while(scanf("%d%d",&n,&m)==2){
        for(int i=1;i<=n;++i){
            ll e;
            scanf("%lld",&e);
            s[i]=s[i-1]+e;
        }
        build(1,1,n);
        printf("Case %d:\n",++kase);
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            Tree ans=query(1,x,y);
            printf("%d %d\n",ans.ansx,ans.ansy);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/wafish/p/10465297.html

你可能感兴趣的:(UVALive 3938 - "Ray, Pass me the dishes!"(线段树区间合并))