题目:

输入输出样例:

解题思路:
单点修改和区间查询问题,而且数据是
5
e
5
5e^5
5e5 的,很自然想到了用线段树来维护。
区
间
最
大
连
续
子
段
和
=
m
a
x
(
左
子
树
的
最
大
连
续
子
段
和
,
右
子
树
的
最
大
连
续
子
段
和
)
区间最大连续子段和=max(左子树的最大连续子段和,右子树的最大连续子段和)
区间最大连续子段和=max(左子树的最大连续子段和,右子树的最大连续子段和)???这样想的话,那么遗漏了一种情况:左端点在左子树,右端点在右子树。所以,最终父节点的答案是左子树,右子树,中间,三者之间最大值。
那么怎么解决中间的情况呢?题目要求是子段是连续的,所以可以维护一个区间从左往右最大和
l
m
a
x
lmax
lmax ,从右往左最大和
r
m
a
x
rmax
rmax 。那么中间的值是不是就等于,左子树的
r
m
a
x
rmax
rmax + 右子树的
l
m
a
x
lmax
lmax 。
其他的建树,更新,修改,查询,就都是常规操作了。
代码:
#include <bits/stdc++.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;
const int MAXN = 5e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int a[MAXN],n,m;
struct SegTree
{
int l,r,data;//区间左端点 区间右端点 区间最大连续字段和
int sum,lmax,rmax;//区间和 区间最大前缀和 区间最大后缀和
}tree[MAXN*4];
void pushup(int rt)
{
tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
tree[rt].lmax=max(tree[rt<<1].lmax,tree[rt<<1].sum+tree[rt<<1|1].lmax);
tree[rt].rmax=max(tree[rt<<1|1].rmax,tree[rt<<1|1].sum+tree[rt<<1].rmax);
tree[rt].data=max(max(tree[rt<<1].data,tree[rt<<1|1].data),tree[rt<<1].rmax+tree[rt<<1|1].lmax);//三者取max
}
void build(int rt,int l,int r)
{
tree[rt].l=l;
tree[rt].r=r;
if(l==r)
{
tree[rt].data=tree[rt].sum=tree[rt].rmax=tree[rt].lmax=a[l];
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void update(int rt,int x,int v)//将第x个的值改为v
{
if(tree[rt].l==tree[rt].r)
{
tree[rt].data=tree[rt].sum=tree[rt].rmax=tree[rt].lmax=v;
return;
}
int mid=(tree[rt].l+tree[rt].r)>>1;
if(x<=mid)
update(rt<<1,x,v);
else
update(rt<<1|1,x,v);
pushup(rt);
}
SegTree ask(int rt,int x,int y)//询问[x,y]区间的连续最大值
{
if(x<=tree[rt].l && tree[rt].r<=y)
return tree[rt];
SegTree nl,nr,ans;
nl.lmax = nl.rmax = nl.sum = nl.data = nr.lmax = nr.rmax = nr.data = nr.sum = -inf;
ans.sum = 0;
int mid=(tree[rt].l+tree[rt].r)>>1;
if(x<=mid)
{
nl = ask(rt << 1, x, y);
ans.sum += nl.sum;
}
if(y>mid)
{
nr = ask(rt << 1 | 1, x, y);
ans.sum += nr.sum;
}
ans.data = max(max(nl.data, nr.data), nl.rmax + nr.lmax);
ans.lmax = max(nl.lmax, nl.sum + nr.lmax);
ans.rmax = max(nr.rmax, nr.sum + nl.rmax);
if(x > mid) ans.lmax = max(ans.lmax, nr.lmax);
if(y <= mid) ans.rmax = max(ans.rmax, nl.rmax);
return ans;
}
int main()
{
qc;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
build(1,1,n);
while(m--)
{
int opt,x,y;
cin>>opt>>x>>y;
if(opt==1)
{
if(x>y)
swap(x,y);
SegTree tmp=ask(1,x,y);
cout<<tmp.data<<endl;
}
else
update(1,x,y);
}
return 0;
}
碎碎念:
其实这个思路想出来并不难,要考虑三种情况然后取max,但是实现起来着实有点费事,怪我写代码能力太差了,orz

本文介绍了如何运用线段树解决一个包含5e5数据的区间最大连续子段和问题。关键在于维护从左往右和从右往左的最大和,以确定中间部分的最值。代码中展示了线段树的建树、更新、查询过程,并给出了完整的C++实现。
1749

被折叠的 条评论
为什么被折叠?



