【题意】
给出一个长度为n的序列,最多选择m段,求能选的最大值。
【思路】
首先我们把同符号的一段累加成一个值。
然后我们选择所有的正数,如果选的个数比m大,就每次减少选的段数,直到数值等于m。
我们将所有的值按绝对值排序(丢进小根堆里),每次挑选出最小的一个。为什么?因为我们要使减少段数后的损失最小。
对于正数,我们将答案减去它,相当于不选这个;
对于负数,我们将答案减去它的绝对值,为什么?因为同符号累加成一段,所以就是(正数 当前绝对值最小的负数 正数),我们相当于选择本来不选的这个负数使相邻正数连起来,段数减小1,答案减少的也是这个数的绝对值。
因此无论这个数是正数还是负数,我们只要选择它,答案的损失就是其绝对值。每次选最小的一个,这样答案损失的最小。
然后我们不选(正数)/选(负数)这个数以后,它与它左边右边的段就相当于连了起来。因此我们要将这个新段丢回堆里,删掉堆里它左边右边的段,以后可能再次被选上这个段。代码中删除实际上是用标记代替,如果当前堆顶被标记了说明这个点实际上应该被删除,就直接踢出这个点。
【代码】
#include<bits/stdc++.h>
#define MAXN 110000
#define mp make_pair
#define pa pair<int,int>
#define inf 0x3f3f3f3f
#define LL long long
#define jcp dalao
using namespace std;
int read()
{
int ans=0,f=1,x;
while(1)
{
x=getchar();
if(x=='-')f=-1;
if(x>='0'&&x<='9')break;
}
while(x>='0'&&x<='9')
{
ans=ans*10+x-'0';
x=getchar();
}
return ans*f;
}
struct cmp
{
bool operator()(pa a,pa b)
{
return abs(a.first)>abs(b.first);
}
};
priority_queue< pa, vector <pa> , cmp > q;
int n,m,tot=0,sum=0,ans=0,a[MAXN],pre[MAXN],nex[MAXN];
bool mark[MAXN];
void del(int x)
{
mark[x]=true;
pre[nex[x]]=pre[x];
nex[pre[x]]=nex[x];
}
int main()
{
n=read();m=read();tot=1;
for(int i=1;i<=n;i++)
{
int x=read();
if((LL)a[tot]*x>=0)a[tot]+=x;
else a[++tot]=x;
}
n=tot;
for(int i=1;i<=tot;i++)if(a[i]>0)sum++,ans+=a[i];
for(int i=1;i<=n;i++)
{
nex[i]=i+1;
pre[i]=i-1;
q.push( mp( a[i] , i ) );
}
memset(mark,false,sizeof(false));
while(sum>m)
{
sum--;
while(mark[q.top().second])q.pop();
int x=q.top().second,l=pre[x],r=nex[x];
q.pop();
if(pre[x]!=0&&nex[x]!=n+1)ans-=abs(a[x]);
else
{
if (a[x]>0)ans-=a[x];
else
{
sum++;
continue;
}
}
a[x]=a[pre[x]]+a[nex[x]]+a[x];
del(pre[x]);del(nex[x]);
q.push(mp(a[x],x));
}
printf("%d\n",ans);
return 0;
}