原题链接:
SPOJ
题意简述
支持两种操作:
- 单点修改
- 求区间最大子段和
长度 5 e 4 5e4 5e4,询问 5 e 4 5e4 5e4,每个数绝对值 1 e 4 1e4 1e4。
数据
输入
n
a1 a2 ... an//长度,数列
q//询问个数
operation//一个询问,
//0 x y 单点修改:位置x变成y
//1 x y 区间[x,y]求最大子段和
输出
对每个1类型的询问,输出答案
样例
输入
4
1 2 3 4
4
1 1 3
0 3 -3
1 2 4
1 3 3
输出
6
4
-3
思路
主要就是我们如何求区间最大子段和。此时,我突然想起,一年前,我刚刚考上我们初中。那是一个阳光明媚的七月。机房里的阵阵空调风,给我一种秋高气爽的感觉,令人心情舒畅。但是我啥都不会,只能在机房里生不如死的熬着。一年过去了,事情繁多,而在记忆的海洋里,在那一段被忘的差不多的记忆区间里,唯一不能忘记的,也是唯一听懂的算法,的就是我们
c
j
q
cjq
cjq老师讲最大子段和的那个时候,讲了一个
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的算法,是用的分治法。思路是这样的:
最大子段和有三种情况:
- 完全在左半边
- 完全在右半边
- 左半边后缀+右半边前缀最大
非常明显,就是这三种。然后由于是分治法做的,我们就珂以考虑丢到线段树上维护。那么,一个节点 [ l , r ] [l,r] [l,r],要维护那些信息呢?
1.要维护前缀,后缀最大和。(考虑到第三种情况)
2. 要维护区间最大子段和(考虑到第1,2种情况)
最大子段和如上转移。前缀,后缀的最大和怎么求呢?
我们发现,前缀最大的和,要么是左半边的前缀最大和,要么是左半边都取上,再加上右半边的前缀最大和。
后缀最大和同理。
这样维护后,加上合并的操作,就珂以快速的求区间最大子段和了。
代码(Music):
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define int long long
#define N 100100
#define MEM(x,a) memset(x,a,sizeof(x))
#define CLS(x) memset(x,0,sizeof(x))
void upx(int &x,int y){x=max(x,y);}
void upn(int &x,int y){x=min(x,y);}
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
struct node
{
int l,r;
int s,x;
int xl,xr;
}tree[N<<2];//一个树上节点
node operator+(node ls,node rs)
{
node ans;
ans.l=ls.l;
ans.r=rs.r;
ans.s=ls.s+rs.s;
ans.x=max(ls.x,rs.x);ans.x=max(ans.x,ls.xr+rs.xl);
ans.xl=max(ls.xl,ls.s+rs.xl);
ans.xr=max(rs.xr,rs.s+ls.xr);
return ans;
}//定义合并的操作
class SegmentTree
{
public:
#define ls index<<1
#define rs index<<1|1
#define L tree[index].l
#define R tree[index].r
#define S tree[index].s
#define X tree[index].x
#define XL tree[index].xl
#define XR tree[index].xr
#define lL tree[ls].l
#define lR tree[ls].r
#define lS tree[ls].s
#define lX tree[ls].x
#define lXL tree[ls].xl
#define lXR tree[ls].xr
#define rL tree[rs].l
#define rR tree[rs].r
#define rS tree[rs].s
#define rX tree[rs].x
#define rXL tree[rs].xl
#define rXR tree[rs].xr
void Update(int index)
{
tree[index]=tree[ls]+tree[rs];
}
void BuildTree(int l,int r,int index)
{
L=l,R=r;
if (l==R)
{
R1(S);
X=XL=XR=S;
return;
}
int mid=(l+r)>>1;
BuildTree(l,mid,ls);
BuildTree(mid+1,r,rs);
Update(index);
// printf("index=%lld\n",index);
// printf("L=%lld,R=%lld\n",L,R);
// printf("datas:%lld %lld %lld %lld\n\n",S,X,XL,XR);
}//非常水的建树
void Change(int pos,int val,int index)
{
if (pos<L or R<pos) return;
if (L==R)
{
S=X=XL=XR=val;
return;
}
Change(pos,val,ls);
Change(pos,val,rs);
Update(index);
}//非常水的单点修改
node Query(int l,int r,int index)
{
if (l<=L and R<=r) return tree[index];
int mid=(L+R)>>1;
if (r<=mid) return Query(l,r,ls);
else if (l>mid) return Query(l,r,rs);
else
{
return Query(l,r,ls)+Query(l,r,rs);
}
}//求区间答案
}T;
int n;
void Input()
{
R1(n);
T.BuildTree(1,n,1);
}
void Soviet()
{
int Q;R1(Q);
for(int i=1;i<=Q;++i)
{
int o;R1(o);
if (o==0)
{
int pos,val;
R1(pos),R1(val);
T.Change(pos,val,1);
}
else if (o==1)
{
int l,r;
R1(l),R1(r);
printf("%lld\n",T.Query(l,r,1).x);
}
}
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
Input();
Soviet();
}
#undef int //long long
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}
博客介绍了如何使用线段树解决SPOJ GSS3问题,支持单点修改和求区间最大子段和。通过分析三种情况,博主详细阐述了利用分治法和线段树维护前缀、后缀及区间最大子段和的方法,并给出了思路和代码实现。
458

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



