ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P4513 小白逛公园

2021-04-29 23:32:57  阅读:191  来源: 互联网

标签:rsum int sum ans 区间 小白 lsum 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;
}

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

标签:rsum,int,sum,ans,区间,小白,lsum,P4513,逛公园
来源: https://www.cnblogs.com/david24/p/14720225.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有