Luogu4513 小白逛公园

原题链接:https://www.luogu.org/problemnew/show/P4513

小白逛公园

题目背景

小新经常陪小白去公园玩,也就是所谓的遛狗啦…

题目描述

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

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

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

输入输出格式
输入格式:

第一行,两个整数 N M M ,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。

接下来 N 行,每行一个整数,依次给出小白 开始时对公园的打分。

接下来 M M 行,每行三个整数。第一个整数 K 1 1 2

K=1 K = 1 表示,小新要带小白出去玩,接下来的两个整数 a a b 给出了选择公园的范围( 1a,bN 1 ≤ a , b ≤ N );

K=2 K = 2 表示,小白改变了对某个公园的打分,接下来的两个整数 p p s ,表示小白对第 p p 个公园的打分变成了 s 1pN 1 ≤ p ≤ N )。
其中, 1N500000 1 ≤ N ≤ 500 000 1M100000 1 ≤ M ≤ 100 000 ,所有打分都是绝对值不超过 1000 1000 的整数。

输出格式:

小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

输入输出样例
输入样例#1:

5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3

输出样例#1:

2
-1

题解

对于每个区间,维护它自己最大子段和。

只有最大子段和显然是无法合并的,我们还需要记录对于每个区间,以左/右端点开头的最大子段,以及自己的区间和,这样就可以合并了。

代码

数据给的区间 [a,b] [ a , b ] 特么居然不保证 ab a ≤ b ,出题人真是 * * 。

#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
using namespace std;
const int M=2e6+5;
struct sd{
    int lm,mx,rm,sum;
    sd(){lm=mx=rm=sum=-1e9;}
};
sd get(int a,int b,int c,int d){sd x;x.lm=a,x.mx=b,x.rm=c,x.sum=d;return x;}
int lm[M],rm[M],mx[M],sum[M],que[M],n,m;
void in(){scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%d",&que[i]);}
void up(int v){sum[v]=sum[ls]+sum[rs],lm[v]=max(lm[ls],sum[ls]+lm[rs]),rm[v]=max(rm[rs],rm[ls]+sum[rs]),mx[v]=max(max(mx[ls],mx[rs]),rm[ls]+lm[rs]);}
void build(int v,int le,int ri)
{
    if(le==ri){sum[v]=lm[v]=rm[v]=mx[v]=que[le];return;}
    int mid=le+ri>>1;build(ls,le,mid);build(rs,mid+1,ri);up(v);
}
sd ask(int v,int le,int ri,int lb,int rb)
{
    if(lb<=le&&ri<=rb){return get(lm[v],mx[v],rm[v],sum[v]);}
    int mid=le+ri>>1;sd ll,rr,ans;
    if(lb<=mid)ll=ask(ls,le,mid,lb,rb);if(mid<rb)rr=ask(rs,mid+1,ri,lb,rb);
    ans.lm=max(ll.lm,ll.sum+rr.lm),ans.rm=max(rr.rm,ll.rm+rr.sum),ans.mx=max(max(ll.mx,rr.mx),ll.rm+rr.lm);
    return ans;
}
void modify(int v,int le,int ri,int pos,int val)
{
    if(le==ri){mx[v]=lm[v]=rm[v]=sum[v]=val;return;}
    int mid=le+ri>>1;if(pos<=mid)modify(ls,le,mid,pos,val);if(mid<pos)modify(rs,mid+1,ri,pos,val);up(v);
}
void ac()
{
    int op,x,y;build(1,1,n);
    while(m--)
    {
        scanf("%d%d%d",&op,&x,&y);
        if(op==1){if(x>y)swap(x,y);printf("%d\n",ask(1,1,n,x,y).mx);}
        else modify(1,1,n,x,y);
    }
}
int main(){in();ac();}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值