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);
}