题意:一个序列$a_{1\cdots n}$,有单点修改和区间查询,查询是给定$k$并询问从$[l,r]$中选取至多$k$个互不相交子区间之和的最大值
有一个不错的思想就是把询问转化为费用流模型
把每个点$i$拆成$i_1,i_2$,连边$(S,i_1,1,0),(i_1,i_2,1,a_i),(i_2,(i+1)_1,1,0),(i_2,T,1,0)$然后跑最大费用最大流
增广$k$次就对应着至多$k$单位流量,也就是至多选取$k$个互不相交子区间之和的最大值
直接做肯定不行,但因为每次增广的过程相当于是找最大子段和并取反,所以我们可以用线段树模拟这个过程
线段树节点上维护$16$个值:区间和,左右端$\min/\max$及其位置,$\min/\max$子段和及其左右端点,还有取反标记
时间复杂度$O(kn\log n)$
#include<stdio.h> #include<algorithm> using namespace std; struct dat{ int s,lmx,lmxp,lmn,lmnp,rmx,rmxp,rmn,rmnp,smx,smxl,smxr,smn,smnl,smnr; dat(int p=0,int v=0){ lmxp=lmnp=rmxp=rmnp=smxl=smxr=smnl=smnr=p; s=lmx=lmn=rmx=rmn=smx=smn=v; } }T[400010]; dat operator+(dat l,dat r){ dat u; u.s=l.s+r.s; if(l.lmx>l.s+r.lmx){ u.lmx=l.lmx; u.lmxp=l.lmxp; }else{ u.lmx=l.s+r.lmx; u.lmxp=r.lmxp; } if(r.rmx>r.s+l.rmx){ u.rmx=r.rmx; u.rmxp=r.rmxp; }else{ u.rmx=r.s+l.rmx; u.rmxp=l.rmxp; } if(l.lmn<l.s+r.lmn){ u.lmn=l.lmn; u.lmnp=l.lmnp; }else{ u.lmn=l.s+r.lmn; u.lmnp=r.lmnp; } if(r.rmn<r.s+l.rmn){ u.rmn=r.rmn; u.rmnp=r.rmnp; }else{ u.rmn=r.s+l.rmn; u.rmnp=l.rmnp; } if(l.smx>r.smx){ u.smx=l.smx; u.smxl=l.smxl; u.smxr=l.smxr; }else{ u.smx=r.smx; u.smxl=r.smxl; u.smxr=r.smxr; } if(l.rmx+r.lmx>u.smx){ u.smx=l.rmx+r.lmx; u.smxl=l.rmxp; u.smxr=r.lmxp; } if(l.smn<r.smn){ u.smn=l.smn; u.smnl=l.smnl; u.smnr=l.smnr; }else{ u.smn=r.smn; u.smnl=r.smnl; u.smnr=r.smnr; } if(l.rmn+r.lmn<u.smn){ u.smn=l.rmn+r.lmn; u.smnl=l.rmnp; u.smnr=r.lmnp; } return u; } void pushup(int x){ T[x]=T[x<<1]+T[x<<1|1]; } int a[100010]; void build(int l,int r,int x){ if(l==r){ T[x]=dat(l,a[l]); return; } int mid=(l+r)>>1; build(l,mid,x<<1); build(mid+1,r,x<<1|1); pushup(x); } int f[400010]; void rev(int x){ dat&u=T[x]; swap(u.lmx,u.lmn); swap(u.lmxp,u.lmnp); swap(u.rmx,u.rmn); swap(u.rmxp,u.rmnp); swap(u.smx,u.smn); swap(u.smxl,u.smnl); swap(u.smxr,u.smnr); f[x]^=1; u.lmx*=-1; u.lmn*=-1; u.rmx*=-1; u.rmn*=-1; u.smx*=-1; u.smn*=-1; u.s*=-1; } void pushdown(int x){ if(f[x]){ rev(x<<1); rev(x<<1|1); f[x]=0; } } void modify(int p,int v,int l,int r,int x){ if(l==r){ T[x]=dat(l,v); return; } pushdown(x); int mid=(l+r)>>1; if(p<=mid) modify(p,v,l,mid,x<<1); else modify(p,v,mid+1,r,x<<1|1); pushup(x); } void reverse(int L,int R,int l,int r,int x){ if(L<=l&&r<=R)return rev(x); pushdown(x); int mid=(l+r)>>1; if(L<=mid)reverse(L,R,l,mid,x<<1); if(mid<R)reverse(L,R,mid+1,r,x<<1|1); pushup(x); } dat query(int L,int R,int l,int r,int x){ if(L<=l&&r<=R)return T[x]; pushdown(x); int mid=(l+r)>>1; if(R<=mid)return query(L,R,l,mid,x<<1); if(mid<L)return query(L,R,mid+1,r,x<<1|1); return query(L,R,l,mid,x<<1)+query(L,R,mid+1,r,x<<1|1); } int L[30],R[30],M; int main(){ int n,m,i,l,r,k,ans; dat t; scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",a+i); build(1,n,1); scanf("%d",&m); while(m--){ scanf("%d%d%d",&i,&l,&r); if(i==0) modify(l,r,1,n,1); else{ scanf("%d",&k); ans=0; while(k--){ t=query(l,r,1,n,1); if(t.smx<=0)break; ans+=t.smx; M++; L[M]=t.smxl; R[M]=t.smxr; reverse(L[M],R[M],1,n,1); } while(M){ reverse(L[M],R[M],1,n,1); M--; } printf("%d\n",ans); } } }