感觉非常神秘啊。
首先需要一个有前途的 做法,然后可以猜到后面就是用线段树维护一下区间信息。
首先区间内所有的 都可以选。现在要在 的间隔中选一些 ,满足前后缀和都非负。
有一堆没有前途的 ,大概是同时维护能有多少个 同时和前后某个 能匹配,感觉不是很能上线段树。
可以把后缀和非负改为前缀和最大值不超过总和。这样就可以从前往后贪心加,维护对 取 max 的前缀和 ,只有 时才能加 。设这个过程中选了 个 和 个 ,历史最大值为 。则这个过程中可能多选了一些 ,要删去这些 使得 。当 时,在取到前缀和历史最大值的位置之后一定有至少一个 ,所以一定可以删去 个 使选出来的数合法。此时总共选了 。
这个就可以上线段树了。 是区间 的数量, 实际上的意义是区间内任意子段的前缀和的最大值,也就是区间内最大子段和。
int n,q,a[maxn];
#define mid ((l+r)>>1)
#define ls nd<<1
#define rs nd<<1|1
struct node{
int num,sum,pre,suf,mx;
}tree[maxn<<2];
node operator*(node u,node v){
return {u.num+v.num,u.sum+v.sum,max(u.pre,u.sum+v.pre),max(v.suf,u.suf+v.sum),max({u.mx,u.suf+v.pre,v.mx})};
}
void build(int nd,int l,int r){
if(l==r){
if(a[l]==1)tree[nd]={1,1,1,1,1};
else tree[nd]={0,-1,0,0,0};
return ;
}
build(ls,l,mid),build(rs,mid+1,r);
tree[nd]=tree[ls]*tree[rs];
}
void modif(int nd,int l,int r,int p){
if(l==r){
if(a[l]==1)tree[nd]={1,1,1,1,1};
else tree[nd]={0,-1,0,0,0};
return ;
}
if(p<=mid)modif(ls,l,mid,p);
else modif(rs,mid+1,r,p);
tree[nd]=tree[ls]*tree[rs];
}
node query(int nd,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr)return tree[nd];
if(qr<=mid)return query(ls,l,mid,ql,qr);
if(ql>mid)return query(rs,mid+1,r,ql,qr);
return query(ls,l,mid,ql,qr)*query(rs,mid+1,r,ql,qr);
}
void work(){
n=read();q=read();
for(int i=1;i<=n;i++)a[i]=read();
build(1,1,n);
while(q--){
int op=read();
if(op==1){
int p=read();a[p]=-a[p];
modif(1,1,n,p);
}
else{
int l=read(),r=read();
node res=query(1,1,n,l,r);
printf("%lld\n",2*res.num-res.mx);
}
}
}