[CF280D]k-Maximum Subsequence Sum

题意:一个序列$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);
		}
	}
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9424729.html