P4513 小白逛公园

1 P4513 小白逛公园

2 题目描述

时间限制 \(1s\) | 空间限制 \(128M\)

在小新家附近有一条“公园路”,路的一边从南到北依次排着 \(n\) 个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。

一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第 \(a\) 个和第 \(b\) 个公园之间(包括 \(a,b\) 两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。

那么,就请你来帮小白选择公园吧。

数据范围:\(N\) 和 \(M\),分别表示表示公园的数量和操作(遛狗或者改变打分)总数, \(1≤N≤500000,1≤M≤1000001≤M≤100000\)

3 题解

我们考虑怎么 \(pushup\)。

如果只是纯粹地将两个子区间的最大连续子区间取 \(max\),那么答案是很不完备的。

我们从底层往上考虑:如果只取 \(max\),那么最终的效果就是求出了区间的最大值,显然这是错误的。我们发现:某个区间的最大连续子区间可能跨越两个子区间。

这个时候我们考虑一个贪心:跨越两个子区间的那个最大连续子区间一定是由左子区间的右侧的最大连续子区间和右子区间的左侧的最大连续子区间拼在一起得到的。这个贪心正确性显然,留给读者自己思考。

此时设 \(lsum\) 表示左侧的最大连续的子区间和,\(rsum\) 表示右侧的最大的连续的子区间和,\(ans\) 为区间的最大连续的区间和,则有

\(p.ans = max(max(p_{lc}.ans, p_{rc}.ans), p_{lc}.rsum + p_{rc}.lsum)\)

我们再来考虑怎么维护区间左侧和右侧的最大连续子区间和。

显然,我们不能只简单地把这个最大子区间和赋值为左子区间的左侧的最大子区间和或者右子区间的右侧的最大子区间和,因为这样我们就没有将另一个区间联系上。我们发现,如果右子区间的左侧的最大子区间和足够大,那么将左子区间和加上右子区间的左侧的最大子区间和很有可能比左子区间左侧的最大子区间和大,答案就应该更新为左子区间和加上右子区间的左侧的最大子区间和。右侧的最大连续子区间和同理。

形象化地说,再设 \(sum\) 表示某区间的区间和。

\(p.lsum = max(p_{lc}.lsum, p_{lc}.sum + p_{rc}.lsum)\)

\(p.rsum = max(p_{rc}.rsum, p_{rc}.sum + p_{lc}.rsum)\)

至于查询时,我们的函数类型为 \(node\),也就是线段树的类型。这样,当目标区间跨越左右两个子区间的时候,我们可以用类似于 \(pushup\) 的方法对答案进行维护,这样一来,我们就可以通过线段树处理复杂的信息了。

4 代码(空格警告):

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 5e5+10;
int n, m, opt, x, y;
struct node
{
    int l, r, lsum, rsum, ans, sum;
}t[N*4];
void pushup(int p)
{
    t[p].sum = t[p*2].sum + t[p*2+1].sum;
    t[p].ans = max(max(t[p*2].ans, t[p*2+1].ans), t[p*2].rsum + t[p*2+1].lsum);
    t[p].lsum = max(t[p*2].lsum, t[p*2].sum + t[p*2+1].lsum);
    t[p].rsum = max(t[p*2+1].rsum, t[p*2+1].sum + t[p*2].rsum);
}
void build(int p, int l, int r)
{
    t[p].l = l;
    t[p].r = r;
    if (l == r)
    {
        scanf("%d", &t[p].sum);
        t[p].lsum = t[p].sum;
        t[p].rsum = t[p].sum;
        t[p].ans = t[p].sum;
        return ;
    }
    int mid = (l+r)/2;
    build(p*2, l, mid);
    build(p*2+1, mid+1, r);
    pushup(p);
}
void modify(int p, int pos, int d)
{
    if (t[p].l == t[p].r)
    {
        t[p].sum = t[p].lsum = t[p].rsum = t[p].ans = d;
        return ;
    }
    int mid = (t[p].l + t[p].r) / 2;
    if (pos <= mid) modify(p*2, pos, d);
    else modify(p*2+1, pos, d);
    pushup(p);
}
node query(int p, int l, int r)
{
    if (t[p].l >= l && t[p].r <= r) return t[p];
    int mid = (t[p].l + t[p].r) / 2, cnt = 0;
    node ans;
    cnt += (l <= mid) + (r > mid);
    if (l <= mid && cnt == 1) return query(p*2, l, r);
    if (r > mid && cnt == 1) return query(p*2+1, l, r);
    node cur, a, b;
    a = query(p*2, l, r);
    b = query(p*2+1, l, r);
    cur.sum = a.sum + b.sum;
    cur.ans = max(max(a.ans, b.ans), a.rsum + b.lsum);
    cur.lsum = max(a.lsum, a.sum + b.lsum);
    cur.rsum = max(b.rsum, b.sum + a.rsum);
    return cur;
}
int main()
{
    scanf("%d %d", &n, &m);
    build(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d %d %d", &opt, &x, &y);
        if (opt == 1)
        {
            if (x > y) swap(x, y);
            node cur = query(1, x, y);
            printf("%d\n", cur.ans);
        }
        else modify(1, x, y);
    }
    return 0;
}

欢迎关注我的微信公众号:智子笔记

P4513 小白逛公园

上一篇:Spectral Sequence, My Homological Saw


下一篇:[NOI2005] 维护数列