传送门https://www.luogu.com.cn/problem/P4513本文参考了董晓老师的博客
这道题着实想了很长时间(新手),只能想到一个O(mn)的dp普通写法,那么遇上区间修改问题改怎么操作呢。答案很明显,线段树!
这道题的线段树主要维护四个信息
区间和,最大左子段和,最大右子段和,与区间最大连续子段和(是不是感觉很眼熟呢),在我的印象中,吉司机线段树(用min直接维护区间)与这题思路相类似。
这里的query还用了一种新的方式来维护
下面直接贴代码(见注释)
// Problem:
// P4513 小白逛公园
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4513
// Memory Limit: 128 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<iostream>
using namespace std;
const int N=5e5+10;
#define lc u<<1
#define rc u<<1|1
int a[N];
struct Tree{
int l,r,sum,lmax,rmax,maxn;//区间和 左区间最大和 右区间最大 最大和
}tr[N*4];
void pushup(Tree &t,Tree a,Tree b){//这里和下面的query是最关键的两个地方
t.sum=a.sum+b.sum;//求和
t.lmax=max(a.lmax,b.lmax+a.sum);//左侧最大来自于左区间左侧和左区间+右区间左侧最大的
t.rmax=max(b.rmax,a.rmax+b.sum);//与上面思路相同
t.maxn=max(max(a.maxn,b.maxn),a.rmax+b.lmax);//来自于下面两边最大的,和中间的(前提连续)
}
Tree query(int u,int l,int r){
if(l<=tr[u].l&&tr[u].r<=r){
return tr[u];//直接返回这个类型
}
int m=(tr[u].l+tr[u].r)>>1;
if(r<=m) return query(lc,l,r);
if(l>m) return query(rc,l,r);
//这种操作与平常相反,原因是我们的线段树只能维护一定的线段,如果遇到断点就要另外处理,于是这里的两行意思是,全部在某一边,没有断点,这样一来就可以直接返回
Tree t;
pushup(t,query(lc,l,m),query(rc,m+1,r));
//这里是有断点的,就以pushup的方式合并一个(本来没有,所以直接开一个新的t)
return t;
}
//需要注意 上面判断是l-r,因为直接包含了,接着找区间就好,下面的是断点,所以直接分开处理
void update(int u,int x,int k){
if(tr[u].l==x&&tr[u].r==x){
tr[u]={x,x,k,k,k,k};
return;
}
int m=(tr[u].l+tr[u].r)>>1;
if(x<=m) update(lc,x,k);
if(x>m) update(rc,x,k);
pushup(tr[u],tr[lc],tr[rc]);
}
void build(int u,int l,int r){
tr[u]={l,r};//这里不能忘了,因为这里我荣幸WA一发
if(l==r){
tr[u]={l,r,a[l],a[l],a[l],a[l]};
return;
}
int m=(l+r)>>1;
build(lc,l,m);
build(rc,m+1,r);
pushup(tr[u],tr[lc],tr[rc]);
}
int main(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
build(1,1,n);
while(m--){
int op,b,c;cin>>op>>b>>c;
if(op==1){
if(b>c) swap(b,c);//交换,swap是c++的自带
cout<<query(1,b,c).maxn<<endl;
}
else{
update(1,b,c);//第b个变成了c
}
}
return 0;
}