51nod 1053 最大M子段和 V2(贪心)

http://www.51nod.com/Challenge/Problem.html#problemId=1053

先把连续的正数合并,连续的负数合并,得到一段新的正负交错的序列
然后选上新序列里所有的正数
如果选的正数个数<=m,直接输出
否则,就需要 扔掉正数 或者 选择负数(即合并2个正数)
无论是哪种操作,带来的损失都是这个数的绝对值,所以可以用一个堆存放绝对值

注意在选择过程中,如果是选择负数,负数在首尾是不能选的,因为不会使段数减少。

#include<queue>
#include<cstdio>
#include<algorithm>

using namespace std;

#define N 500003

int a[N];
long long c[N];
int l[N],r[N];

bool tra[N];

struct cmp
{
	bool operator () (const int x,const int y)
	{
		return abs(c[x])>abs(c[y]);
	}
};
priority_queue<int,vector<int>,cmp>q;

int main()
{
	int n,m,k=0,p=0;
	long long s=0,ans=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	bool tag=1;
	for(int i=1;i<=n;++i)
		if(1ll*a[i]*a[i-1]<0)
		{
			if(s>0) 
			{
				ans+=s;
				++p;
			}
			c[++k]=s;
			s=a[i];
		}
		else s+=a[i];
	if(s>0) 
	{
		ans+=s;
		++p;
	}
	if(s) c[++k]=s;
	r[1]=2;
	q.push(1);
	for(int i=2;i<k;++i) 
	{
		l[i]=i-1;
		r[i]=i+1;
		q.push(i);
	}
	l[k]=k-1;
	q.push(k);
	int le=p-m;
	if(le<=0)
	{
		printf("%lld",ans);
		return 0;
	}
	int now;
	while(1)
	{
		now=q.top();
		q.pop();
		if(tra[now]) continue;
		if(c[now]<=0 && (!l[now] || !r[now])) continue;
		ans-=abs(c[now]);
		//printf("-----%lld\n",ans);
		c[now]+=c[l[now]]+c[r[now]];
		if(l[now])
		{
			tra[l[now]]=true;
			l[now]=l[l[now]];	
			r[l[now]]=now;
		}
		if(r[now])
		{
			tra[r[now]]=true;
			r[now]=r[r[now]];
			l[r[now]]=now;
		}
		q.push(now); 
		--le;
		if(!le) break;
	}
	printf("%lld",ans);
}
posted @ 2021-04-09 19:48  TRTTG  阅读(100)  评论(0编辑  收藏  举报