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) };

题目要求区间,类似的:

用线段树维护最大连续区间max_sub、最大前缀右端点max_prefix、最大后缀左端点max_suffix.

详细操作见代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define lc rt<<1
#define rc rt<<1|1

const int maxn = 500000 + 5;
typedef long long LL;
LL num[maxn], max_prefix[maxn<<2], max_suffix[maxn<<2];

struct node{
    int l, r;
    node(int ll=0, int rr=0):l(ll),r(rr){}
} max_sub[maxn<<2];

LL prefix_sum[maxn];
int  ql, qr;

LL sum(int l, int r){
    return prefix_sum[r] - prefix_sum[l-1];
}
LL sum(node a){
    return sum(a.l, a.r);
}
node better(node a, node b){
    if(sum(a) != sum(b)) return sum(a) > sum(b) ? a:b;
    return (a.l<b.l||(a.l==b.l&&a.r<b.r))? a:b;
}
void build(int rt, int l, int r){
    if(l==r){
        max_prefix[rt] = max_suffix[rt] = l;
        max_sub[rt] = node(l,l);
        return ;
    }

    int m = (l+r)>>1;
    build(lc, l, m);
    build(rc, m+1, r);
    LL v1 = sum(l, max_prefix[lc]);
    LL v2 = sum(l, max_prefix[rc]);
    if(v1 == v2) max_prefix[rt] = min(max_prefix[lc], max_prefix[rc]);
    else max_prefix[rt] = v1 > v2 ? max_prefix[lc] : max_prefix[rc];
    v1 = sum(max_suffix[lc], r);
    v2 = sum(max_suffix[rc], r);
    if(v1 == v2) max_suffix[rt] = min(max_suffix[lc], max_suffix[rc]);
    else max_suffix[rt] = v1 > v2 ? max_suffix[lc] : max_suffix[rc];
    max_sub[rt] = better(max_sub[lc], max_sub[rc]);
    max_sub[rt] = better(max_sub[rt], node(max_suffix[lc], max_prefix[rc]));
}

int query_prefix(int rt, int l, int r){
    if(qr >= max_prefix[rt]) return max_prefix[rt];
    int m = (l+r)>>1;
    //l<=qr<=m
    if(qr <= m) return query_prefix(lc, l, m);
    //m+1<=qr<=r
    int rr = query_prefix(rc, m+1, r);
    node ret = better(node(l,rr), node(l, max_prefix[lc]));
    return ret.r;
}

int query_suffix(int rt, int l, int r){
    if(ql <= max_suffix[rt]) return max_suffix[rt];
    int m = (l+r)>>1;
    //m+1<=ql<=r
    if(ql > m) return query_suffix(rc, m+1, r);
    //l<=ql<=m
    int ll = query_suffix(lc, l, m);
    node ret = better(node(ll, r), node(max_suffix[rc], r));
    return ret.l;
}

node query(int rt, int l, int r){
    if(ql <= l && r <= qr) return max_sub[rt];
    int m = (l+r)>>1;

    if(qr <= m) return query(lc, l, m);

    if(ql >  m) return query(rc, m+1, r);

    //ql <= m <= qr
    int ll = query_suffix(lc, l, m);  //l_max_suffix
    int rr = query_prefix(rc, m+1, r); //r_max_prefix
    node mid = node(ll, rr);
    node sub = better( query(lc, l, m), query(rc, m+1, r));
    return better( mid, sub);
}
int main()
{
    int n, m, i, cas = 1, l, r;
    while(~scanf("%d%d", &n, &m)){
        printf("Case %d:\n", cas++);
        prefix_sum[0] = 0;
        for(i=1; i<=n; ++i) {
                scanf("%lld", &num[i]);
                prefix_sum[i] = prefix_sum[i-1] + num[i];
        }
        node v = node(1,3);
        build(1, 1, n);
        while(m--){
            scanf("%d%d", &l, &r);
            ql = l; qr = r;
            node ans = query(1, 1, n);
            printf("%d %d\n", ans.l, ans.r);
        }
    }
    return 0;
}

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

时间: 2024-10-13 20:50:03

UVA-1400 Ray, Pass me the Dishes, LA 3938 , 线段树,区间查询的相关文章

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!

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