题目大意:给定一长度为n的环形序列求最大连续和,连续和长度不能超过n-1,单点修改。
最大连续和?我会动规/贪心
环形序列?我会乱搞
带修改?我会线段树
限制长度?!@#¥%……&*()
我们先忽略掉环形序列
首先,限制很特殊,是n-1,于是我们可以很简单地求出[1,n-1]和[2,n]的最大连续和,取两者的最大值。(不要问我限制为k的时候怎么做,我不会啊)
然后我们看环形序列,首先考虑静态做法,嗯,看来我以前都是乱搞的TAT,按照我以前的做法,发现当前长度超过n时就把尾巴砍砍砍。
考虑最大连续和的对偶问题:最小连续和。
显然,环形序列的序列和减去最小连续和就是最大连续和了。
这里我们不妨规定最小连续和的最短长度为1,于是最大连续和的最大长度为n-1。
至此,此题差不多就解决了。
于是套用线段树的带修改最大连续和的方法,同样维护最小连续和,详见代码。
#include<iostream>
#include<cstdio>
#include<cstring>
#define lc o<<1
#define rc o<<1|1
using namespace std;
const int N=100000+5;
struct solver{
int sum,lx,rx,li,ri,mx,mi;
};
solver operator + (solver l,solver r){
solver ans;
ans.sum=l.sum+r.sum;
ans.lx=max(l.lx,l.sum+r.lx);
ans.rx=max(r.rx,r.sum+l.rx);
ans.li=min(l.li,l.sum+r.li);
ans.ri=min(r.ri,r.sum+l.ri);
ans.mx=max(l.rx+r.lx,max(l.mx,r.mx));
ans.mi=min(l.ri+r.li,min(l.mi,r.mi));
return ans;
}
struct Node{
int l,r;
solver ans;
}tr[N<<2];
int a[N],n;
void pushup(int o){
tr[o].ans=tr[lc].ans+tr[rc].ans;
}
solver newsol(int x){
solver ans=(solver){x,x,x,x,x,x,x};
return ans;
}
void build(int o,int l,int r){
tr[o].l=l;tr[o].r=r;
if(l==r)tr[o].ans=newsol(a[l]);
else{
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(o);
}
}
solver query(int o,int a,int b){
int l=tr[o].l,r=tr[o].r;
if(a==l&&r==b)return tr[o].ans;
else{
int mid=l+r>>1;
if(b<=mid)return query(lc,a,b);
else if(mid<a)return query(rc,a,b);
else return query(lc,a,mid)+query(rc,mid+1,b);
}
}
void update(int o,int p,int v){
int l=tr[o].l,r=tr[o].r;
if(l==r)tr[o].ans=newsol(v);
else{
int mid=l+r>>1;
if(p<=mid)update(lc,p,v);
else update(rc,p,v);
pushup(o);
}
}
int solve(){
int ans=tr[1].ans.sum-tr[1].ans.mi;
ans=max(ans,query(1,1,n-1).mx);
ans=max(ans,query(1,2,n).mx);
return ans;
}
int main(){
//freopen("a.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
int m;scanf("%d",&m);
while(m--){
int p,v;scanf("%d%d",&p,&v);
update(1,p,v);
printf("%d\n",solve());
}
return 0;
}