洛谷2048 BZOJ2006 NOI2010 超级钢琴 线段树 堆 贪心

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/forever_shi/article/details/89133027

题目链接

题意:
给你一个长度为nn的序列,一个区间的权值是区间内的权值和,你要选择kk个不相同长度在[l,r][l,r]的区间,使得这kk个区间总权值和最大。n,k<=5e5n,k<=5e5

题解:
算是送我退役的题吧。如果一轮省选之前我做了这个题,可能结果就会不一样了吧,可惜没有如果。

显然是找长度在[l,r][l,r]区间内的最大的kk个区间作为答案。那么考虑怎么找这前kk大的。我们预处理一个前缀和,这样区间和就变成了前缀和相减的形式。我们的思路是,对于每一个左端点,合法的右端点是一个连续的区间,而对于左端点是ii的区间,前缀和相减的形势一定是s[j]s[i1]s[j]-s[i-1],这时候对于一个确定的iis[i1]s[i-1]是确定的,那么我们只需要找合法区间内一个最大的s[j]s[j]。那么我们用线段树维护一个区间前缀和的最大值,就可以快速找到每个位置为左端点的权值和最大的一个合法区间了。

而所有可能的区间中最大的一定是在每个区间为左端点求出的最大的区间中产生。我们用一个大根堆来维护这些区间的区间和。然后我们考虑拿出这个最大的区间后,原来的那个左端点还有哪些右端点可能成为最大的区间。我们发现如果原来可行的右端点区间是[i,j][i,j],前缀和最大的位置在midmid,那么之后可能的区间是[i,mid1][i,mid-1][mid+1,r][mid+1,r]。我们分别再求出这两个区间中前缀和最大的,放入堆中。这样每个区间被取出来之后会放进去两个区间,区间总数还是O(k)O(k)量级的。

最终复杂度是O(klogn+klongk)O(klogn+klongk),堆和线段树的复杂度是并行的。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,k,L,R,a[500010];
long long ans,s[500010];
struct node
{
	int l,r,i,id;
	long long x;
	node (long long xx=0,int ll=0,int rr=0,int ii=0,int iid=0)
	{
		x=xx;
		l=ll;
		r=rr;
		i=ii;
		id=iid;
	}
};
bool operator < (node x,node y){return x.x<y.x;}
struct tree
{
	int l,r,ji;
	long long mx;
}tr[2000010];
priority_queue<node> q;
inline int read()
{
	int x=0,f=1;
	char s=getchar();
	while(s>'9'||s<'0')
	{
		if(s=='-')
		f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x*f;
}
inline void build(int rt,int l,int r)
{
	tr[rt].l=l;
	tr[rt].r=r;
	if(l==r)
	{
		tr[rt].mx=s[l];
		tr[rt].ji=l;
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	if(tr[rt<<1].mx>tr[rt<<1|1].mx)
	{
		tr[rt].mx=tr[rt<<1].mx;
		tr[rt].ji=tr[rt<<1].ji;
	}
	else
	{
		tr[rt].mx=tr[rt<<1|1].mx;
		tr[rt].ji=tr[rt<<1|1].ji;
	}
}
inline tree query(int rt,int le,int ri)
{
	int l=tr[rt].l,r=tr[rt].r;
	if(le<=l&&r<=ri)
	return tr[rt];
	int mid=(l+r)>>1;
	tree qwq,res;
	res.mx=-1e18;
	if(le<=mid)
	{
		qwq=query(rt<<1,le,ri);
		if(qwq.mx>res.mx)
		res=qwq;
	}
	if(mid+1<=ri)
	{
		qwq=query(rt<<1|1,le,ri);
		if(qwq.mx>res.mx)
		res=qwq;
	}
	return res;
}
int main()
{
	n=read();
	k=read();
	L=read();
	R=read();
	for(int i=1;i<=n;++i)
	{
		a[i]=read();
		s[i]=s[i-1]+a[i];
	}
	build(1,1,n);
	for(int i=0;i<=n-L;++i)
	{
		tree ovo=query(1,i+L,min(n,i+R));
		node qwq(ovo.mx-s[i],i+L,min(n,i+R),ovo.ji,i);
		q.push(qwq);
	}
	while(k)
	{
		node qwq=q.top(),QAQ;
		q.pop();
		ans+=qwq.x;
		tree ovo;
		if(qwq.i+1<=qwq.r)
		{
			ovo=query(1,qwq.i+1,qwq.r);
			QAQ.x=ovo.mx-s[qwq.id];
			QAQ.l=qwq.i+1;
			QAQ.r=qwq.r;
			QAQ.i=ovo.ji;
			QAQ.id=qwq.id;
			q.push(QAQ);
		}
		if(qwq.l<=qwq.i-1)
		{
			ovo=query(1,qwq.l,qwq.i-1);
			QAQ.x=ovo.mx-s[qwq.id];
			QAQ.l=qwq.l;
			QAQ.r=qwq.i-1;
			QAQ.i=ovo.ji;
			QAQ.id=qwq.id;
			q.push(QAQ);
		}
		--k;
	}
	printf("%lld\n",ans);
	return 0;
}
展开阅读全文

没有更多推荐了,返回首页