动态最大连续和 LA 3938 线段树

题目地址:https://vjudge.net/problem/UVALive-3938

简单翻译:

给你一个长度为n的整数序列,和m和询问。对于询问(x,y)需要找到两个下标i,j。使得x<=i<=j<=y。整数序列中i位置到j位置的数的和最大。如果有多个满足条件的i,j。取i最小的那个。如果还有多个解,j应该最小

该题其实就是一个动态最大连续和的问题,与之对应的有静态最大连续和。

我们先来简单了解一下静态最大连续和

静态最大连续和

给你一个整数序列,求其最大和最大的子序列。

这题有两种解法:

1.动态规划

2.分治

这两种解法有不了解的可以在网上进行搜索。我们需要了解的是分治这种解法,解决动态最大连续和需要利用到其中的部分思想。

分治法:最优解要么全部在左半边序列,要么全部在右半边序列,要么一半在左边一半在右边。

 

动态最大连续和

动态与静态的最大区别就是,我们不是求整个序列的最大子序列,而是对于每个询问求出其最大子序列。

像这种区间询问多次,很多都是线段树。这里肯定也就会用到线段树。

我们构造一个线段树,线段树中保存的信息为maxSub(最大连续和),maxPrefix(最大前缀和),maxSuffix(最大后缀和),最大连续和的左端点l,右端点r。最大前缀和的结束位置,最大后缀和的开始位置,当前区间所有数的总和。

那么现在又有一个问题了,如何用线段树两个子节点的信息求出父节点呢?

1.maxSub

与静态连续和的思想类似,有三种情况

1.当前父节点的最大连续和是其左子节点的最大连续和

2.当前父节点的最大连续和是其右子节点的最大连续和

3.当前父节点的最大连续和一部分在左边一部分在右边。和为其左子节点的最大后缀和加上右子节点的最大前缀和

2.maxPrefix

两种情况

1.当前父节点的最大前缀和是其左子节点的最大前缀和

2.当前父节点的最大前缀和是其左子节点的总和和右子节点的的最大前缀和

3.maxSuffix

与2一致

 

至于题中说的的有多个满足的情况,解决比较简单,这里就不进行分析了

代码实现

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值