版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目
题目大意
给出一个序列 和 次询问,每次询问的类型如下:
0 i val
,表示执行将 的值改为 ;1 l r k
,表示选出 的 个不相交的子串,使得它们的 值之和最大。
分析
对于每个询问,执行
次如下操作:求
的最大子段和,加进ans
,然后把该字段所有元素变成它的相反数。当然,这个过程中如果
的最大子段和出现了负数,直接退出。输出答案。
玄学直观上这个做法很好理解,相当于很多次求前缀和的过程。据说可以用网络流证。
线段树要实现两个功能:求最大子段和,区间取相反数。
其实求第一个只需要维护最大值的三坨东西,但是由于第二个东西,你还要维护最小值的三坨东西,于是加上懒标记你一共要维护16个东西。
Sum
:区间和;MaxBgS
和MaxBgPos
:最大前缀和,最大前缀和对应的右端点;MinBgS
和MinBgPos
:最小前缀和,最小前缀和对应的右端点;MaxEdS
和MaxEdPos
:最大后缀和,最大后缀和对应的左端点;MinEdS
和MinEdPos
:最小后缀和,最小后缀和对应的左端点;MaxSbS
,MaxSbL
和MaxSbR
:最大子段和,最大子段和对应的左右端点;MinSbS
,MinSbL
和MaxinR
:最小子段和,最小子段和对应的左右端点;flag
:区间取反的懒标记。
PushUp
和Query
有点恶心,PushUp
你看看代码就知道了,Query
要返回一个结点,不能只返回一个值,所以Query
里面要调用这个变异的PushUp
。
代码
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
int read(){
int x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') f|=c=='-',c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return f?-x:x;
}
#define MAXN 100000
#define INF 0x7fffffff
int N,A[MAXN+5];
struct SegmentTree{
#define lch (i<<1)
#define rch (i<<1|1)
struct Node{
int l,r;
int Sum;
int MaxBgS,MinBgS,MaxBgPos,MinBgPos;
int MaxEdS,MinEdS,MaxEdPos,MinEdPos;
int MaxSbS,MinSbS,MaxSbL,MaxSbR,MinSbL,MinSbR;
bool flag;
inline void Rev(){
//区间取相反数,有点巧妙
Sum=-Sum,flag^=1;
swap(MaxBgS,MinBgS),MaxBgS=-MaxBgS,MinBgS=-MinBgS,swap(MaxBgPos,MinBgPos);
swap(MaxEdS,MinEdS),MaxEdS=-MaxEdS,MinEdS=-MinEdS,swap(MaxEdPos,MinEdPos);
swap(MaxSbS,MinSbS),MaxSbS=-MaxSbS,MinSbS=-MinSbS,swap(MaxSbL,MinSbL),swap(MaxSbR,MinSbR);
}
inline void Set(int x,int pos){
//区间赋值,只针对单位区间
Sum=MaxBgS=MinBgS=MaxEdS=MinEdS=MaxSbS=MinSbS=x;
MaxBgPos=MinBgPos=MaxEdPos=MinEdPos=MaxSbL=MaxSbR=MinSbL=MinSbR=pos;
}
}Tree[(MAXN<<2)+5];
inline void PushUp(Node &A,Node B,Node C){
//调不出来先检查这里,Min和Max极可能写错
A.Sum=B.Sum+C.Sum;
//最大子段和
if(max(B.MaxSbS,C.MaxSbS)>B.MaxEdS+C.MaxBgS){
if(B.MaxSbS>C.MaxSbS)
A.MaxSbS=B.MaxSbS,A.MaxSbL=B.MaxSbL,A.MaxSbR=B.MaxSbR;
else
A.MaxSbS=C.MaxSbS,A.MaxSbL=C.MaxSbL,A.MaxSbR=C.MaxSbR;
}
else
A.MaxSbS=B.MaxEdS+C.MaxBgS,A.MaxSbL=B.MaxEdPos,A.MaxSbR=C.MaxBgPos;
//最小子段和
if(min(B.MinSbS,C.MinSbS)<B.MinEdS+C.MinBgS){
if(B.MinSbS<C.MinSbS)
A.MinSbS=B.MinSbS,A.MinSbL=B.MinSbL,A.MinSbR=B.MinSbR;
else
A.MinSbS=C.MinSbS,A.MinSbL=C.MinSbL,A.MinSbR=C.MinSbR;
}
else
A.MinSbS=B.MinEdS+C.MinBgS,A.MinSbL=B.MinEdPos,A.MinSbR=C.MinBgPos;
//最大前缀和
if(B.Sum+C.MaxBgS>B.MaxBgS)
A.MaxBgS=B.Sum+C.MaxBgS,A.MaxBgPos=C.MaxBgPos;
else
A.MaxBgS=B.MaxBgS,A.MaxBgPos=B.MaxBgPos;
//最小前缀和
if(B.Sum+C.MinBgS<B.MinBgS)
A.MinBgS=B.Sum+C.MinBgS,A.MinBgPos=C.MinBgPos;
else
A.MinBgS=B.MinBgS,A.MinBgPos=B.MinBgPos;
//最大后缀和
if(C.Sum+B.MaxEdS>C.MaxEdS)
A.MaxEdS=C.Sum+B.MaxEdS,A.MaxEdPos=B.MaxEdPos;
else
A.MaxEdS=C.MaxEdS,A.MaxEdPos=C.MaxEdPos;
//最小后缀和
if(C.Sum+B.MinEdS<C.MinEdS)
A.MinEdS=C.Sum+B.MinEdS,A.MinEdPos=B.MinEdPos;
else
A.MinEdS=C.MinEdS,A.MinEdPos=C.MinEdPos;
}
inline void PushDown(int i){
if(Tree[i].flag){
Tree[i].flag=0;
Tree[lch].Rev(),Tree[rch].Rev();
}
}
void Build(int i,int l,int r){
Tree[i].l=l,Tree[i].r=r;
if(l==r){
Tree[i].Set(A[l],l);
return;
}
int mid=(l+r)>>1;
Build(lch,l,mid);
Build(rch,mid+1,r);
PushUp(Tree[i],Tree[lch],Tree[rch]);
}
Node Query(int i,int l,int r){
if(l<=Tree[i].l&&Tree[i].r<=r)
return Tree[i];
PushDown(i);
int mid=(Tree[i].l+Tree[i].r)>>1;
if(mid+1<=l)
//这个mid+1我调了一晚上,,,,,,,
//因为我平常写的线段树不是这样的,,,,,,,,
//我一般是在上面特判return
//但是这道题不行,,,,,,,,,,,,
return Query(rch,l,r);
if(r<=mid)
return Query(lch,l,r);
Node ret,L=Query(lch,l,r),R=Query(rch,l,r);
PushUp(ret,L,R);
return ret;
}
void Update(int i,int l,int r){
if(r<Tree[i].l||Tree[i].r<l)
return;
if(l<=Tree[i].l&&Tree[i].r<=r){
Tree[i].Rev();
return;
}
PushDown(i);
Update(lch,l,r);
Update(rch,l,r);
PushUp(Tree[i],Tree[lch],Tree[rch]);
}
void Modify(int i,int x,int pos){
if(pos<Tree[i].l||Tree[i].r<pos)
return;
if(Tree[i].l==Tree[i].r){
Tree[i].Set(Tree[i].flag?-x:x,pos);
return;
}
PushDown(i);
Modify(lch,x,pos);
Modify(rch,x,pos);
PushUp(Tree[i],Tree[lch],Tree[rch]);
}
}T;
#define MAXK 20
pair<int,int> R[MAXK+5];
int main(){
int N=read();
for(int i=1;i<=N;i++)
A[i]=read();
T.Build(1,1,N);
int Q=read();
while(Q--){
int opt=read();
if(opt==0){
int pos=read(),val=read();
T.Modify(1,val,pos);
}
else{
int Left=read(),Right=read(),K=read(),Ans=0,cnt=0;
while(K--){
SegmentTree::Node tmp=T.Query(1,Left,Right);
if(tmp.MaxSbS<=0)
break;
Ans+=tmp.MaxSbS;
R[++cnt]=make_pair(tmp.MaxSbL,tmp.MaxSbR);
T.Update(1,tmp.MaxSbL,tmp.MaxSbR);
}
for(int i=1;i<=cnt;i++)
T.Update(1,R[i].first,R[i].second);
//复原
printf("%d\n",Ans);
}
}
}