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

题目链接: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_sub,以及两边max_suffix+max_prefix

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
#define lson(x) ((x)<<1)
#define rson(x) (((x)<<1)+1)
typedef long long ll;
const int maxn = 500005;
const ll INF = 0x3f3f3f3f3f3f3f3f;

struct segment {
    int l, r;
    ll val;
    segment (int l = 0, int r = 0, ll val = 0) {
        this->set(l, r, val);
    }
    void set (int l, int r, ll val) {
        this->l = l;
        this->r = r;
        this->val = val;
    }
    segment operator + (const segment& u) {
        return segment(min(l, u.l), max(r, u.r), val + u.val);
    }
    bool operator < (const segment& u) const {
        if (val != u.val)
            return val < u.val;
        if (l != u.l)
            return l > u.l;
        return r > u.r;
    }
};

struct Node {
    int l, r;
    segment max_sub, max_prefix, max_suffix;
} node[4*maxn];
int N, M;
ll arr[maxn], s[maxn];

Node seg_push (Node a, Node b) {

    Node ret;

    ll suml = s[a.r] - s[a.l-1];
    ll sumr = s[b.r] - s[b.l-1];

    ret.l = a.l;
    ret.r = b.r;
    ret.max_sub = max(a.max_suffix + b.max_prefix, max(a.max_sub, b.max_sub));
    ret.max_prefix = max(a.max_prefix, segment(a.l, a.r, suml) + b.max_prefix);
    ret.max_suffix = max(b.max_suffix, a.max_suffix + segment(b.l, b.r, sumr));
    return ret;
}

void build_segTree (int root, int l, int r) {

    if (l == r) {
        node[root].l = node[root].r = l;
        node[root].max_sub.set(l, r, (ll)arr[l]);
        node[root].max_prefix.set(l, r, (ll)arr[l]);
        node[root].max_suffix.set(l, r, (ll)arr[l]);
        return;
    }

    int mid = (l + r) / 2;
    build_segTree(lson(root), l, mid);
    build_segTree(rson(root), mid + 1, r);

    node[root] = seg_push(node[lson(root)], node[rson(root)]);
}

Node query (int root, int l, int r) {

    if (l <= node[root].l && r >= node[root].r)
        return node[root];

    int mid = (node[root].l + node[root].r) / 2;
    if (l <= mid && r > mid)
        return seg_push(query(lson(root), l, r), query(rson(root), l, r));
    else if (r <= mid)
        return query(lson(root), l, r);
    else
        return query(rson(root), l, r);
}

int main () {
    int cas = 1;
    while (scanf("%d%d", &N, &M) == 2) {
        s[0] = 0;
        for (int i = 1; i <= N; i++) {
            scanf("%lld", &arr[i]);
            s[i] = s[i-1] + arr[i];
        }

        build_segTree(1, 1, N);
        printf("Case %d:\n", cas++);

        int l, r;
        for (int i = 0; i < M; i++) {
            scanf("%d%d", &l, &r);
            Node u = query(1, l, r);
            printf("%d %d\n", u.max_sub.l, u.max_sub.r);
        }
    }
    return 0;
}
时间: 2024-12-20 12:01:28

uva 1400 - "Ray, Pass me the dishes!"(线段树)的相关文章

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

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

LA3938:&quot;Ray, Pass me the dishes!&quot;(线段树)

Description After doing Ray a great favor to collect sticks for Ray, Poor Neal becomes very hungry. In return for Neal's help, Ray makes a great dinner for Neal. When it is time for dinner, Ray arranges all the dishes he makes in a single line (actua

uvalive 3938 &quot;Ray, Pass me the dishes!&quot; 线段树 区间合并

题意:求q次询问的静态区间连续最大和起始位置和终止位置 输出字典序最小的解. 思路:刘汝佳白书 每个节点维护三个值 pre, sub, suf 最大的前缀和, 连续和, 后缀和 然后这个题还要记录解的位置所以还要区间总和sum 1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cstdio> 6 #include

LA3938 &quot;Ray, Pass me the dishes!&quot; (线段树区间合并)

题目:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=22105 题意:给定整数n和m,给出一个n个元素的序列,查询m次给定区间[L,R]的最大连续和的位置[x,y],有多个区间输出x最小的,还有多个的话输出y最小的. 分析:每个节点存8个信息,最大连续和.最大后缀和.最大前缀和.区间和.前缀末位置.后缀首位置.最大连续和的首位置和末位置. 最大连续和=max(lson最大连续和,rson最大连续和,lson最大后缀+rso

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

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