题目要求实现单点修改和区间查询,区间查询结果为一段区间的最大子段和,查询一段区间的最大子段和的时间复杂度为O(n),所以考虑线段树
接下来考虑改如何选择线段树中维护的值,从而可以实现从两个儿子推出父亲的最大子段和
首先对于儿子一定有最大子段和的值,既然有最大子段和,我们要推出父亲的最大子段和还需要什么值?
我们对两个儿子的最大子段和取max是否就可以得到父亲的最大子段和?结果显然不是,因为两个儿子组合在一块还会在两个儿子中间形成新的子段,所以我们不仅要考虑两个儿子原先的最大子段和,还需要考虑中间部分的子段是否又组合出了新的更大的子段和,而对于这一部分子段,我们只需要维护两个儿子的最大前缀和以及最大后缀和就可以得出结果,让左儿子的最大后缀和加上右儿子的最大前缀和,再与两个儿子的最大子段和取max就可以得到父亲的最大子段和
然后我们再考虑最大前缀和以及最大后缀和如何pushup,由于两者是对称关系,所以我们先只考虑最大前缀和,显然父亲的最大前缀和分为两种,一种是不包括在右儿子中,一种是包括在右儿子中,对于第一种即为左儿子的最大前缀和,对于第二种,即为左儿子的和加上右儿子的最大前缀和,同理可得最大后缀和,这样就维护出了最大前缀和以及最大后缀和
ac代码:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair<int,int> pii;
const int N=5e5+10;
struct Tree
{
int l,r;
int sum,left,right,res;
}tr[N<<2];
int w[N];
int op,l,r,n,m;
void pushup(Tree &u,Tree &l,Tree &r)
{
u.sum=l.sum+r.sum;
u.left=max(l.left,l.sum+r.left);
u.right=max(r.right,r.sum+l.right);
u.res=max(max(l.res,r.res),l.right+r.left);
}
void pushup(int u)
{
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u]={l,r,w[l],w[l],w[l],w[l]};
return ;
}
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int pos,int d)
{
if(tr[u].l==tr[u].r)
{
tr[u].res=tr[u].sum=d;
tr[u].left=tr[u].right=d;
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(pos<=mid)modify(u<<1,pos,d);
else modify(u<<1|1,pos,d);
pushup(u);
}
Tree query(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r)return tr[u];
int mid=tr[u].l+tr[u].r>>1;
if(l>mid)return query(u<<1|1,l,r);
else if(r<=mid)return query(u<<1,l,r);
else
{
Tree ans;
auto left=query(u<<1,l,r);
auto right=query(u<<1|1,l,r);
pushup(ans,left,right);
return ans;
}
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>w[i];
build(1,1,n);
while(m--)
{
cin>>op>>l>>r;
if(op==1)
{
if(l>r)swap(l,r);
cout<<query(1,l,r).res<<endl;
}
else modify(1,l,r);
}
}
signed main()
{
Mirai;
int T=1;
//cin>>T;
while(T--)
{
solve();
}
}