CF280D k-Maximum Subsequence Sum【线段树】

题目链接

碎碎念

刚开始:这怎么是个黑题啊,就是一个线段树而已啦。

然后,这道题我断断续续调了两天。

所以说,(flag)不要立得太早。

题目解析

如果题目没有说选(k)段不相交,而是只有一段的话,那就直接求最大子段和。

但是这道题可以选(k)段的话,就相当于我们可以在最大子段和里断开(k-1)处((k-1)段),把一些会拖累答案的段退回去不要。

考虑反悔贪心。

我们取一个最大子段和出来,然后把选中的区间取反。

重复这样的操作(k)次就可以得到答案。

为什么呢?

如果这一次选中的区间与之前选过的(取反了的)区间没有交集,相当于我们再选了新的一段。

如果与之前选过的有交集,而我们之前又取了反,那就相当于把原来选的那一段给退了回去。如果两段区间(指新选的区间和原来选过的区间)不是包含关系,显然段数会多(1)。如果是包含关系,就相当于把原来的区间退回去,而新选的区间裂开了,所以区间总数还是多(1)

如果把一个区间取反,最大子段和变最小子段和(的相反数) 。

所以同时维护一个最小子段和

或者写两个线段树 :一个维护正常的,一个维护相反数。

如果修改,就交换这两个线段树的对应结点(感觉这种好写多了,本来刚开始维护了最小子段和,但最后还是屈服喏(qwq),并且优化了一下写法,把原来丑陋无比的(200+)的代码压得好看一点了。


►Code View

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define MOD 998244353
#define INF 0x3f3f3f3f
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
	return f*x;
}
int n,k;
pair<int,int> fx[N];
int cnt;//记录下来被修改(取反)过的区间 每次查询时要返回来
int a[N];
struct node{
	int sum,mxs,lmx,rmx;
	int l,r,rr,ll;
}tree[2][N<<2];
bool tag[N<<2];//tag拿出来 合并的时候就不用记下来再搞回去 
node Merge(node x,node y)
{
	node t;
	t.sum=x.sum+y.sum;
	t.mxs=t.lmx=t.rmx=-INF;
	
	if(x.lmx>t.lmx) t.lmx=x.lmx,t.rr=x.rr;
	if(x.sum+y.lmx>t.lmx) t.lmx=x.sum+y.lmx,t.rr=y.rr;
	
	if(y.rmx>t.rmx) t.rmx=y.rmx,t.ll=y.ll;
	if(y.sum+x.rmx>t.rmx) t.rmx=y.sum+x.rmx,t.ll=x.ll;
	
	if(x.mxs>t.mxs) t.mxs=x.mxs,t.l=x.l,t.r=x.r;
	if(y.mxs>t.mxs) t.mxs=y.mxs,t.l=y.l,t.r=y.r;
	if(x.rmx+y.lmx>t.mxs) t.mxs=x.rmx+y.lmx,t.l=x.ll,t.r=y.rr;//Cao 这里打成x.rr 
	
	return t;
}
void PushUp(int i)
{
	tree[0][i]=Merge(tree[0][i<<1],tree[0][i<<1|1]);
	tree[1][i]=Merge(tree[1][i<<1],tree[1][i<<1|1]);
}
void Init(int i,int l,int val)
{
	tree[0][i].sum=tree[0][i].mxs=tree[0][i].lmx=tree[0][i].rmx=val;
	tree[1][i].sum=tree[1][i].mxs=tree[1][i].lmx=tree[1][i].rmx=-val;
	tree[0][i].l=tree[0][i].r=tree[0][i].rr=tree[0][i].ll=l;
	tree[1][i].l=tree[1][i].r=tree[1][i].rr=tree[1][i].ll=l;
}
void Build(int i,int l,int r)
{
	if(l==r)
	{
		Init(i,l,a[l]);
		return ;
	}
	int mid=(l+r)>>1;
	Build(i<<1,l,mid);
	Build(i<<1|1,mid+1,r);
	PushUp(i);
}
void Shift(int i)
{
	swap(tree[0][i],tree[1][i]);
	tag[i]^=1;
}
void PushDown(int i)
{
	if(tag[i]==1)
	{
		Shift(i<<1);
		Shift(i<<1|1);
		tag[i]=0;
	}
}
void Update(int i,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)
	{
		Shift(i);
		return ;
	}
	PushDown(i);
	int mid=(l+r)>>1;
	if(ql<=mid) Update(i<<1,l,mid,ql,qr);
	if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr);
	PushUp(i);
}
node Query(int i,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr) return tree[0][i];
	PushDown(i);
	int mid=(l+r)>>1;
	if(qr<=mid) return Query(i<<1,l,mid,ql,qr);
	else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr);
	return Merge(Query(i<<1,l,mid,ql,qr),Query(i<<1|1,mid+1,r,ql,qr));
}
void Modify(int i,int l,int r,int pos,int val)
{
	if(l==r)
	{
		Init(i,l,val);
		return ;
	}
	PushDown(i);
	int mid=(l+r)>>1;
	if(pos<=mid) Modify(i<<1,l,mid,pos,val);
	else Modify(i<<1|1,mid+1,r,pos,val);
	PushUp(i);
}
int main()
{
	n=rd();
	for(int i=1;i<=n;i++)
		a[i]=rd();
	Build(1,1,n);
	int Q=rd();
	while(Q--)
	{
		int opt=rd();
		if(opt==0)
		{
			int i=rd(),val=rd();
			Modify(1,1,n,i,val);
		}
		else
		{
			int l=rd(),r=rd(),k=rd();
			int res=0;
			while(k--)
			{
				node ans=Query(1,1,n,l,r);
				if(ans.mxs<=0) break;
				res+=ans.mxs;
				fx[++cnt]=make_pair(ans.l,ans.r);
				Update(1,1,n,ans.l,ans.r);
			}
			printf("%d
",res);
			for(int i=cnt;i>=1;i--)
				Update(1,1,n,fx[i].first,fx[i].second);
			cnt=0;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/lyttt/p/14063796.html