传送门:bzoj2288
题解
将正负相同的连续区间缩成一块,数列就转成了正负相间的了。
设为正的有
n
n
n段且和为
s
u
m
sum
sum。
若
n
≤
m
n\leq m
n≤m答案就是
s
u
m
sum
sum,
否则每次取出绝对值最小的一块
a
i
a_i
ai(除两端的负块)将其与旁边的块合并,使得:
n
−
1
,
s
u
m
−
∣
a
i
∣
n-1,sum-|a_i|
n−1,sum−∣ai∣
(抛掉正块/选择负块)
代码
#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(){
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);
}