Description
ftiasch 18岁生日的时候,lqp18_31给她看了一个神奇的序列 A1, A2, …, AN. 她被允许选择不超过 M 个连续的部分作为自己的生日礼物。
自然地,ftiasch想要知道选择元素之和的最大值。你能帮助她吗?
Input
第1行,两个整数 N (1 ≤ N ≤ 105) 和 M (0 ≤ M ≤ 105), 序列的长度和可以选择的部分。
第2行, N 个整数 A1, A2, …, AN (0 ≤ |Ai| ≤ 104), 序列。
Output
一个整数,最大的和。
Sample Input
5 2
2 -3 2 -1 2
Sample Output
5
解题思路:
首先连续符号相同的数可合并在一起,这样序列就是正负交替出现了。
求出大于0的段数cnt以及和sum;
若cnt<=m,那sum就是答案。
若cnt>m,那么每次取绝对值最小的一段,并将它与左右两端合并,用堆+链表维护。
如果该段是负数,那么相当于强行合并两段正数,损失最小,提高了利用价值;
如果该段是正数,那么相当于不选它或是削弱两边负数的影响,有利于后面的合并。
两种情况都会使cnt-1,ans-|a[i]|。
注意如果取得是首尾处的负数要直接扔掉,且cnt不会减。
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005;
int n,m,cnt,ans,l[N],r[N],a[N];
bool mark[N];
priority_queue<pii,vector<pii>,greater<pii> >q;
void del(int x)
{
mark[x]=1;
l[r[x]]=l[x],r[l[x]]=r[x];
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),m=getint();
int x,tmp=0;
for(int i=1;i<=n;i++)
{
x=getint();if(!x)continue;
if(1ll*a[tmp]*x>0)a[tmp]+=x;
else a[++tmp]=x;
}n=tmp;
for(int i=1;i<=n;i++)
{
if(a[i]>0)cnt++,ans+=a[i];
l[i]=i-1,r[i]=i+1;
q.push(make_pair(abs(a[i]),i));
}
while(cnt>m)
{
while(mark[q.top().second])q.pop();
int x=q.top().second;q.pop();
if((l[x]&&r[x]!=n+1)||(a[x]>0))
{
--cnt,ans-=abs(a[x]);
a[x]+=a[l[x]]+a[r[x]];
q.push(make_pair(abs(a[x]),x));
del(l[x]),del(r[x]);
}
else del(x);
}
printf("%d",ans);
}