普通的线段树
题面:一个环上每一个点都有一个权值,要求支持单点更新,查询最大连续子序列和
ps:查询的时候子序列的大小不能超过n,也就是不能把所有的数都选起来
如果不是环上,是区间上的话,这题就解决了(如果不知道怎么做的话,看这里
这个题和之前的那个题差别在于这题是一个环上的(废话!
如果要特判的话,那人生也太可悲了
如果换一个角度呢,也就是说我们考虑如果所求的区间是从1和n这两个地方穿过的
这个时候的答案是不是就等于
∑i=1na[i]−(1到n的最小连续子序列和)
呢
这种情况就处理完了,然后还有要注意的是不能全取也不能全不取
思考一下什么情况下会全取:(所有数都是正数的时候
这个时候区间和就等于区间最大连续子序列,需要返回的是所有数的和-最小的那个数
同时值得注意的是,有时候不全是正数但是也会有区间和等于区间最大连续子序列的情况,考虑如下数据
3 4 5 -3 -4 -5 3 4 5
这个时候应该返回的是区间和-区间最小连续子序列(想一想,为什么
全不取也同样思考一下,会发现这个时候所有的节点都是负数
那么这个时候我们应该返回最小的那个点
剩下的情况就是max(最大连续子序列,区间和-最小连续子序列)
顺便再安利一波这种重载加号来表示区间合并的方法,感觉十分及其的好用
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 112345;
#define LL int
struct Info{
LL lmax,rmax;
LL lmin,rmin;
LL nmin,nmax;
LL smax,smin;
LL sum;
void init(LL v){
sum = v;
smax = lmax = rmax = nmax = v;
smin = lmin = rmin = nmin = v;
}
LL value(){
if(smax == sum){
return sum - smin;
}
if(smin == sum){
return nmax;
}
return max(smax,sum - smin);
}
};
Info operator + (const Info &L,const Info &R){
Info ret;
ret.nmin=min(L.nmin,R.nmin);
ret.nmax=max(L.nmax,R.nmax);
ret.lmax=max(L.lmax,L.sum+R.lmax);
ret.rmax=max(R.rmax,R.sum+L.rmax);
ret.lmin=min(L.lmin,L.sum+R.lmin);
ret.rmin=min(R.rmin,R.sum+L.rmin);
ret.sum = L.sum+R.sum;
ret.smax = max(max(L.smax,R.smax),L.rmax+R.lmax);
ret.smin = min(min(L.smin,R.smin),L.rmin+R.lmin);
return ret;
}
#define root 1,1,n
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define Mid int m = l + (r-l)/2
#define Now int o,int l,int r
Info arr[maxn*4];
void update(Now,int pos,int v){
if(l==r){
arr[o].init(v);
return;
}
Mid;
if(pos <= m){
update(lson,pos,v);
}
else{
update(rson,pos,v);
}
arr[o] = arr[o<<1] + arr[o<<1|1];
}
LL query(){
return arr[1].value();
}
int main(){
int n;
while(~scanf("%d",&n)){
LL x;
for(int i=1;i<=n;i++){
scanf("%d",&x);
update(root,i,x);
}
int m;
scanf("%d",&m);
int pos;
while(m--){
scanf("%d %d",&pos,&x);
update(root,pos,x);
printf("%d\n",query());
}
}
return 0;
}