题目描述
https://www.luogu.com.cn/problem/SP1716
You are given a sequence A of N (N <= 50000) integers between -10000 and 10000. On this sequence you have to apply M (M <= 50000) operations:
modify the i-th element in the sequence or for given x y print max{Ai + Ai+1 + .. + Aj | x<=i<=j<=y }.
输入格式
The first line of input contains an integer N. The following line contains N integers, representing the sequence A1..AN.
The third line contains an integer M. The next M lines contain the operations in following form:
0 x y: modify Ax into y (|y|<=10000).
1 x y: print max{Ai + Ai+1 + .. + Aj | x<=i<=j<=y }.
输出格式
For each query, print an integer as the problem required.
题意翻译
nn 个数,qq 次操作
操作0 x y
把A_xAx 修改为yy
操作1 l r
询问区间[l, r][l,r] 的最大子段和
感谢 @Edgration 提供的翻译
输入输出样例
输入 #1复制
4 1 2 3 4 4 1 1 3 0 3 -3 1 2 4 1 3 3
输出 #1复制
6 4 -3
题意:求区间最大子段和的经典题目
对于一段区间,我们要查询这段区间的最大子段和,这个最大子段和会出现在以下三种情况
- 完全包含在(l,mid)中
- 完全包含在 (mid+1,r)中
- 同时包含在(l,mid)的后连续一段以及(mid+1,r)的前连续一段
对于前两种情况,假设根节点为记p,tree[p].dat=max(tree[p*2].dat,tree[p*2+1].dat);其中tree[x].dat代表完全覆盖区间的最大子段和
对于第三种情况,它的值为左区间的后缀和与右区间的前缀和最大。记区间最大前缀和为pref,区间最大后缀和为suf
那么此时 tree[p].dat=max(max(tree[p*2].dat,tree[p*2+1].dat),suf[p*2]+pref[p*2+1];
那么现在的问题是如何维护pref和suf
对于一个区间[l,r],其最大前缀和可能是:
1.[l,mid]内的最大前缀和
2.整个sum[l,mid]+区间[mid+1,r]内的最大前缀和
也就是pref(l,r)=max(pref(l,mid),sum(l,mid)+pref(mid+1,r));
同理,对于一个区间[l,r]其最大后缀和可能是
1.[mid+1,r]内的最大后缀和suf[mid+1,r];
2.整个sum[mid+1,r]加上suf[l,mid];
也就是suf(l,r)=max(suf(mid+1,r),sum(mid+1,r)+suf(l,mid);
对于这个区间和sum,就是正常的区间加减了
最后要说的就是这题的ask操作:
首先当查询的区间完全覆盖节点区间,直接返回节点区间的区间最大和
然后如果l<=mid,继续递归查询左半子树完全覆盖的区间
然后如果r>mid,继续递归查询右半子树完全覆盖的区间
如果刚好比如说[1,10]里面查[4,8]:
先判断是否完全覆盖–》不是完全覆盖
再判断占两个区间的(【4,8】占了[1,5]和[6,10]两个区间,先递归找左半区间的子树;
if(l<=mid),返回 查询到的在左子节点的值,即返回查询[1,3]的结果;
if(mid<r),返回 查询到的在右子节点的值,即返回查询[4,5]的结果,而[4,5]是完全覆盖的,所以直接返回
再rc查询[6,10]这个区间;
if(l<=mid) 返回 查询到的在左子节点的值 ,即返回[6,8]的结果,完全覆盖,直接返回子树。
现在lc和rc已经是包含找到的[4,5],[6,8]的子树的区间所有结果了
再对这个结果进行 tree[p].dat=max(max(tree[p*2].dat,tree[p*2+1].dat),suf[p*2]+pref[p*2+1]的维护就好了
SeamentTree ask(LL p,LL l,LL r)
{
if(l<=l(p)&&r>=r(p))//完全覆盖时直接返回
{return tree[p];}
LL mid=(l(p)+r(p))/2;
if(l<=mid&&mid<r)
{
SeamentTree lc=ask(p*2,l,r),rc=ask(p*2+1,l,r),self;//比如在[1,10]里面找[4,8],先dfs lc,发现[1,5]不能满足[4,8]的if条件,往下接着找
self.sum=self.lmax=self.rmax=self.dat=0;//初始化
self.sum=lc.sum+rc.sum;
self.lmax=max(lc.lmax,lc.sum+rc.lmax);
self.rmax=max(rc.rmax,rc.sum+lc.rmax);
self.dat=max(lc.dat,rc.dat);
self.dat=max(self.dat,lc.rmax+rc.lmax);
return self;
}
if(l<=mid) return ask(p*2,l,r);
if(r>mid) return ask(p*2+1,l,r);
}
完整代码:
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=500100;
typedef long long LL;
LL a[maxn];
struct SeamentTree
{
LL l,r;LL dat;LL sum;LL lmax;LL rmax;
//dat--区间最大子段连续和;sum--区间和; lmax--紧靠左端的最大连续子段和; rmax--紧靠右端的最大连续字段和
#define l(x) tree[x].l
#define r(x) tree[x].r
#define dat(x) tree[x].dat
#define sum(x) tree[x].sum
#define lmax(x) tree[x].lmax
#define rmax(x) tree[x].rmax
}tree[maxn*4];
void build(LL p,LL l,LL r)
{
l(p)=l;r(p)=r;
if(l==r) {sum(p)=a[l];dat(p)=a[l];lmax(p)=a[l];rmax(p)=a[l];return;}
LL mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
sum(p)=sum(p*2)+sum(p*2+1);
lmax(p)=max(lmax(p*2),sum(p*2)+lmax(p*2+1));
rmax(p)=max(rmax(p*2+1),rmax(p*2)+sum(p*2+1));
dat(p)=max(dat(p*2),dat(p*2+1));
dat(p)=max(dat(p),rmax(p*2)+lmax(p*2+1));
}
void change(LL p,LL x,LL d)
{
if(l(p)==r(p))
{
sum(p)=d;dat(p)=d;lmax(p)=d;rmax(p)=d;return;
}
LL mid=(l(p)+r(p))/2;
if(x<=mid) change(p*2,x,d);
else change(p*2+1,x,d);
sum(p)=sum(p*2)+sum(p*2+1);
lmax(p)=max(lmax(p*2),sum(p*2)+lmax(p*2+1));
rmax(p)=max(rmax(p*2+1),rmax(p*2)+sum(p*2+1));
dat(p)=max(dat(p*2),dat(p*2+1));
dat(p)=max(dat(p),rmax(p*2)+lmax(p*2+1));
}
SeamentTree ask(LL p,LL l,LL r)
{
if(l<=l(p)&&r>=r(p))
{return tree[p];}
LL mid=(l(p)+r(p))/2;
if(l<=mid&&mid<r)
{
SeamentTree lc=ask(p*2,l,r),rc=ask(p*2+1,l,r),self;
self.sum=self.lmax=self.rmax=self.dat=0;//初始化
self.sum=lc.sum+rc.sum;
self.lmax=max(lc.lmax,lc.sum+rc.lmax);
self.rmax=max(rc.rmax,rc.sum+lc.rmax);
self.dat=max(lc.dat,rc.dat);
self.dat=max(self.dat,lc.rmax+rc.lmax);
return self;
}
if(l<=mid) return ask(p*2,l,r);
if(r>mid) return ask(p*2+1,l,r);
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n,q;cin>>n;
for(LL i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
cin>>q;
while(q--)
{
LL op,x,y;
cin>>op>>x>>y;
if(op==0)
{
change(1,x,y);
}
else
{
if(x>y) swap(x,y);//
cout<<ask(1,x,y).dat<<endl;
}
}
return 0;
}