UVA-1400 Ray, Pass me the dishes!

UVA-1400 Ray, Pass me the dishes!

题意:给出一个长度为n的整数序列D,有m个询问,每个询问(a,b)找到两个下标x和y,使得a<=x<=y<=n,并且Dx+...+Dy尽量大。如果有多组满足条件的x和y,x应该尽量小。如果还有多解,y应该尽量小。

思路:线段树,对于一个区间,需要维护的值是这个区间的最大连续前缀和,后缀和以及最大连续和的数字下标出现位置。这样,当找(a,b)的最大连续和的下标位置时,分三种情况:

1:最大连续和的起点和终点都在[a,mid)中

2:连续和的起点终点都在[mid,b)中

3:起点在[a,mid)中,终点在[mid,b)中,这样最大连续和就是以mid为终点的[a,mid)的后缀和+以mid为起点的[mid,b)的前缀和,递归查找连续和的起点终点即可。

具体参考《算法竞赛入门经典——训练指南》P202

AC代码:

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include <string>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAX 0x7ffffffffffffff
typedef long long ll;
const int N_MAX = 500000+20;
int D[N_MAX];
ll presum[N_MAX];
int n, m;
struct MAX_sum {
    ll data;int L, R;
    MAX_sum(ll data = 0, int L = 0, int R = 0) :data(data), L(L), R(R) {}
}max_sum[2 * N_MAX];
struct MAX_prefix {
    ll data;int R;
    MAX_prefix(ll data = 0, int R = 0) :data(data), R(R) {}
}max_prefix[2 * N_MAX];
struct MAX_suffix {
    ll data;int L;
    MAX_suffix(ll data = 0, int L = 0) :data(data), L(L) {}
}max_suffix[2 * N_MAX];

ll sum(int l,int r) {//计算区间[l,r)的和
    if (l == 0)return presum[r-1];
    else return presum[r-1] - presum[l - 1];
}

MAX_sum better(MAX_sum a,MAX_sum b) {
    if (a.data != b.data)return a.data > b.data ? a : b;
    else if (a.L != b.L)return a.L < b.L ? a : b;
    else return a.R < b.R ? a : b;
}

void segtree_init(int k, int l, int r) {//区间[l,r)
    if (r - l == 1) {
        max_prefix[k] = MAX_prefix(D[l], l);
        max_suffix[k] = MAX_suffix(D[l], l);
        max_sum[k] = MAX_sum(D[l], l, l);
    }
    else {
        int mid = (l + r) >> 1, chl = (k << 1) + 1, chr = (k << 1) + 2;
        segtree_init(chl, l, mid);
        segtree_init(chr, mid, r);

        if (max_prefix[chl].data >= sum(l,mid) + max_prefix[chr].data)
            max_prefix[k] = max_prefix[chl];
        else max_prefix[k] = MAX_prefix(sum(l,mid) + max_prefix[chr].data, max_prefix[chr].R);
        if (max_suffix[chr].data > max_suffix[chl].data + sum(mid,r))
            max_suffix[k] = max_suffix[chr];
        else max_suffix[k] = MAX_suffix(sum(mid,r) + max_suffix[chl].data, max_suffix[chl].L);
        max_sum[k] = better(max_sum[chl],max_sum[chr]);
        max_sum[k] = better(max_sum[k], MAX_sum(max_suffix[chl].data + max_prefix[chr].data, max_suffix[chl].L, max_prefix[chr].R));
    }
}

MAX_prefix seg_pre_query(int a, int b, int k, int l, int r) {
    if (a <= l&&b >= r) {
        return max_prefix[k];
    }
    else if (a >= r || b <= l) {//完全不相交
        return MAX_prefix(-MAX, INF);
    }
    else {
      int chl = (k << 1) + 1, chr = (k << 1) + 2, mid = (l + r) >> 1;
      MAX_prefix  p1=seg_pre_query(a, b, chl, l, mid);
      MAX_prefix  p2=seg_pre_query(a, b, chr, mid, r);
      return p1.data >= p2.data + sum(l, mid) ? p1 : MAX_prefix(p2.data + sum(l, mid), p2.R);
    }
}

MAX_suffix seg_suf_query(int a,int b,int k,int l,int r) {
    if (a <= l&&b >= r) {
        return max_suffix[k];
    }
    else if (a >= r || b <= l) {
        return MAX_suffix(-MAX,INF);
    }
    else {
        int chl = (k << 1) + 1, chr = (k << 1) + 2, mid = (l + r) >> 1;
        MAX_suffix  p1 = seg_suf_query(a, b, chl, l, mid);
        MAX_suffix  p2 = seg_suf_query(a, b, chr, mid, r);
        return p2.data > p1.data + sum(mid, r) ? p2 : MAX_suffix(p1.data + sum(mid, r), p1.L);
    }
}

MAX_sum  seg_query(int a, int b, int k, int l, int r) {
    if (a <= l&&b >= r) {
        return max_sum[k];
    }
    else if (a >= r || b <= l) {//完全不相交
        return MAX_sum(-MAX,INF,INF);
    }
    else {
        int mid = (l + r) >> 1, chl = (k << 1) + 1, chr = (k << 1) + 2;
        MAX_sum sum1 = seg_query(a, b, chl, l, mid);
        MAX_sum sum2 = seg_query(a, b, chr,mid, r);
        MAX_prefix pre = seg_pre_query(a, b, chr, mid, r);//mid一定在[a,b)区间当中
        MAX_suffix suf = seg_suf_query(a, b, chl, l, mid);//同理
        MAX_sum sum3 = MAX_sum(pre.data + suf.data, suf.L, pre.R);
        MAX_sum SUM = better(sum1, sum2);
        SUM = better(SUM,sum3);
        return SUM;
    }
}

int main() {
    int Case = 0;
    while (scanf("%d%d",&n,&m)!=EOF) {
        for (int i = 0; i < n; i++) {
            scanf("%d", &D[i]);
            if (!i)presum[i] = D[i];
            else presum[i] = presum[i - 1] + D[i];
        }
        segtree_init(0,0,n);
        printf("Case %d:\n",++Case);
        for (int cs = 1; cs <= m; cs++) {
            int a, b; scanf("%d%d", &a, &b); a--;
            MAX_sum ans=seg_query(a,b,0,0,n);
            printf("%d %d\n",ans.L+1,ans.R+1);
        }
    }
    return 0;
}

/*
8 1
3 -4 1 3 -2 5 1 3
2 6
*/

原文地址:https://www.cnblogs.com/ZefengYao/p/9123820.html

时间: 2024-08-30 05:50:55

UVA-1400 Ray, Pass me the dishes!的相关文章

uva 1400 - &quot;Ray, Pass me the dishes!&quot;(线段树)

题目链接:uva 1400 - "Ray, Pass me the dishes!" 题目大意:给定一个长度为n个整数序列,对m次询问作出回答,对于每次询问(a,b),找到两个下标x,y使得x到y的连续和为区间a,b中最大的连续和,如果存在多解优先x小,然后y小. 解题思路:线段树,对于每个节点维护三个线段值: max_sub:区间连续最大和 max_prefix:区间连续前缀最大和 max_suffix:区间连续后缀最大和 建树的过程维护三个值,查询时只需考虑左右子节点的max_su

Uva 1400 &quot;Ray, Pass me the dishes!&quot; ( 线段树 + 区间查询 )

Uva  1400 "Ray, Pass me the dishes!" (线段树 + 区间查询) 题意: 给顶一个长度为n的整数序列D,我们的任务是对m的询问做出回答对于询问(a,b),需要找到两个下标x和y,是的 a <= x <= y <=b并且Dx+...........Dy 尽量大. x,y尽量小 分析: 这题是做线段树比较好的一题,大白书上介绍的是维护了三个域,maxsub,maxpre,maxsuf这里也可以只维护两个域,再最后再考虑跨区间的问题这里没有

UVA - 1400&quot;Ray, Pass me the dishes!&quot;(线段树)

题目链接 题目大意:给你N个数字,要求你动态的给出L到R之间,X>= L && Y<=R,使得X,Y这段的连续和是LR之间的最大连续和,如果有多解,输出X小的,接着是Y小的. 解题思路:结点保存三个附加线段,max_sub, max_suffix, max_prefix.对于每次查询最大的连续和要不出现在左子树的max_sub, 要不就是右子树的max_sub, 要不就是左子树的max_suffix + 右子树的max_prefix.然后每次查询的时候都返回一个新的节点,是新的

UVA 1400 1400 - &quot;Ray, Pass me the dishes!&quot;(线段树)

UVA 1400 - "Ray, Pass me the dishes!" 题目链接 题意:给定一个序列,每次询问一个[L,R]区间,求出这个区间的最大连续子序列和 思路:线段树,每个节点维护3个值,最大连续子序列,最大连续前缀序列,最大连续后缀序列,那么每次pushup的时候,根据这3个序列去拼凑得到新的一个结点即可 代码: #include <cstdio> #include <cstring> #include <algorithm> usin

UVA 1400 1400 - &amp;quot;Ray, Pass me the dishes!&amp;quot;(线段树)

UVA 1400 - "Ray, Pass me the dishes!" 题目链接 题意:给定一个序列,每次询问一个[L,R]区间.求出这个区间的最大连续子序列和 思路:线段树,每一个节点维护3个值.最大连续子序列.最大连续前缀序列,最大连续后缀序列,那么每次pushup的时候,依据这3个序列去拼凑得到新的一个结点就可以 代码: #include <cstdio> #include <cstring> #include <algorithm> us

&quot;Ray, Pass me the dishes!&quot;

uvaLive3938:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1939 题意:给你n个数,然后给你一个区间,让你查询这个区间内最大和连续子区间. 题解:哎.智商是硬伤啊,虽然线段树也做过不少题目,但是遇到这样的题目还是不会处理啊,看了别人的代码才明白了怎么做.用那个线段树维护区间最大前缀,最大后缀,以及真正的最大区间

UVAlive - 3938 —— &quot;Ray, Pass me the dishes!&quot; 【线段树】

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=22105 #include <iostream> #include <cstdio> #include <cstring> #include <string> #define INF 0x3f3f3f3f #define lson rt<<1, l, m #define rson rt<<1|1, m+1, r u

UVALive3938 &quot;Ray, Pass me the dishes!&quot; 线段树动态区间最大和

AC得相当辛苦的一道题,似乎不难,但是需要想仔细, 开始的时候的错误思路----是受之前做过的区间最长连续子串影响http://blog.csdn.net/u011026968/article/details/38357157 区间合并的时候,我直接按照---如果(左子树的最大前缀和长度==左子树的长度 && 右子树的前缀和>0),就合并左前缀,这想法有两个错误:1.右子树的前缀和==0的时候是不是要合并区间呢?题目要求x尽量小,如果x相同y尽量小(x是ans的区间左端点,y是Ans

UVA-1400 Ray, Pass me the Dishes, LA 3938 , 线段树,区间查询

题意:给出一列数(n个),m次查询区间[l,r]的最大连续区间[x,y](l<=x<=y<=r).(n,m<=500 000) 思路:动态查询区间最大连续区间: 如果是求最大连续区间和: 用线段树维护最大连续和sum_sub.最大前缀和sum_prefix.最大后缀和sum_suffix. root.sum_sub = max{l.sum_sub, r.sum_sub, (l.sum_suffix + r.sum_prefix) }; 题目要求区间,类似的: 用线段树维护最大连续区