UVaLive 3938 "Ray, Pass me the dishes!" (线段树求动态最大连续和)

本文介绍了一种使用线段树算法高效求解整数序列中最大连续和及其位置的方法。通过递归构建和查询线段树,维护最大前缀和、最大后缀和、最大连续和及区间和等关键值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:求一段整数序列中最大连续续和的左右端点的坐标。

解题思路:因为需要高效的求出一段的区间和,考虑使用线段树算法,从分治的角度来思考这个问题,这个最大的连续和有可能在一段区间的左半区间也有可能在右半区间也有可能贯穿了两个区间。

      也就是说我们需要构造一个线段树,并同时维护四个值,最大前缀和,最大后缀和,最大连续和,区间和

      这三个值都存在着与子节点的递推关系,利用线段树自底向上求解。

      考虑任意一个非叶子节点(叶子节点所有参数等于自身)

      最大前缀和 = max (左子树最大前缀,左子树区间和+右子树最大前缀)

      最大后缀和 = max (右子树最大后缀,右子树区间和+左子树最大后缀)

      最大连续和 = max (左子树最大连续和,右子树最大连续和,左子树最大后缀+右子树最大前缀)

      明确了递推过程,剩下的就是编码实现和细节处理了,本题要特别注意一下符号的处理(>= or >)

AC代码:

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

#define maxn 500005
typedef long long ll;
int score[maxn];
int a,b;
char ch;
struct NODE
{
    ll suf,sub,pre,sum;//max_suffix max_prefix max_sub(最大子区间连续和)
    int l,r;      // the left pos and the right pos of the max_sub
    int sl;      //sl->the left pos of max_suffix
    int pr;      //pr->the right pos of max_prefix

} node[4*maxn];

NODE zero = { -(1ll<<40), -(1ll<<40), -(1ll<<40), 0};//node针对update这种运算的零元

void update(NODE &u, NODE &v1, NODE &v2)//将两个子区间的性质合并向上传递
{

    if(v1.pre >= v1.sum + v2.pre){
        u.pre = v1.pre; u.pr = v1.pr;
    }
    else{
        u.pre = v1.sum + v2.pre; u.pr = v2.pr;
    }

    if(v2.suf > v1.suf + v2.sum){// pay attention > or >=
        u.suf = v2.suf; u.sl = v2.sl;
    }
    else{
        u.suf = v1.suf + v2.sum; u.sl = v1.sl;
    }

    if(v1.sub >= v2.sub){ // pay attention > or >=
        u.sub = v1.sub; u.l = v1.l; u.r = v1.r;
    }
    else{
        u.sub = v2.sub; u.l = v2.l; u.r = v2.r;
    }

    if( (u.sub < v1.suf + v2.pre) ||
       (u.sub == v1.suf + v2.pre && (u.l>v1.sl || (u.l==v1.sl && u.r>v2.pr)))){
           u.sub = v1.suf + v2.pre; u.l = v1.sl; u.r = v2.pr;
    }

    u.sum = v1.sum + v2.sum;
}

void build(ll root, ll l, ll r)
{
    if(l==r){
        NODE &u = node[root];
        u.l = u.r = u.pr = u.sl = r;
        u.sum = u.pre = u.suf = u.sub = score[l];
        return;
    }
    ll mid = (l+r)>>1;
    build(root<<1, l, mid);
    build(root<<1 | 1 ,mid+1, r);
    update(node[root], node[root<<1], node[root<<1 | 1]);
}

NODE query(ll root, ll l, ll r)
{
    if(l>b || r<a )//无交集
        return zero;
    if(a<=l && r<=b)//此区间包含root所管理的区间
        return node[root];
    int mid = (l+r)>>1;
    NODE L = query(root<<1, l, mid);//部分相交
    NODE R = query(root<<1 | 1 ,mid+1, r);
    NODE ans;
    update(ans, L, R);
    return ans;
}


int main()
{
    int n,m;
    int kase = 1;
    while(~scanf("%d%d",&n,&m))
    {
        printf("Case %d:\n",kase++);
        for(int i=1; i<=n; i++) scanf("%d",score+i);
        build(1,1,n);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&a,&b);
            NODE ans = query(1,1,n);
            printf("%d %d\n",ans.l,ans.r);
        }
    }
    //cout << "Hello world!" << endl;
    return 0;
}

上面的写法需要构造一个NODE元素针对update运算的单位元,当然query函数还有其他的写法,也就是不同的区间比较策略。

NODE query(ll root, ll l, ll r)
{
    if(a<=l && r<=b)
        return node[root];
    int mid = (l+r)>>1;
    NODE ans;
    if(a <= mid && b > mid){
        NODE L = query(root<<1, l, mid); NODE R = query(root<<1 | 1, mid + 1, r);
        update(ans, L, R);
        return ans;
    }
    if(b<=mid){
        return query(root<<1, l, mid);
    }
    return query(root<<1 | 1, mid+1, r);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值