传送门
把同号的连续一段合并起来。
那么现在一定是正负交替出现。
令正的总和为sum。
如果正段数<=m,直接输出sum。
如果大于m,那么就要通过选或删来达到m段。
把这m段取绝对值扔到一个堆里。每次取绝对值最小的那个,用sum减去这个值。
如果这个数是正数,相当于是不要这一段,段数少1。
如果这个数是负数,相当于要取这一段,把两边两个正的连起来了,段数少1。
然后把这个数和两边的数合并起来,丢回去。用链表维护。
合并之后保证了后面的每次操作都会使段数少1。
这样可以保证每次操作的损失都最小。
如果当前数是在序列的两端且是负的,那么一定不取它,直接delete掉。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
int n,m,x,tot,a[maxn],num,ans;
int nxt[maxn],pre[maxn],vis[maxn];
struct node{
int pos,val;
node(){}
node(int _pos,int _val){pos=_pos,val=_val;}
friend inline bool operator<(const node &a,const node &b){return a.val>b.val;}
}t[maxn];
priority_queue<node> Q;
inline int sign(int x){return x>=0;}
inline void del(int x){vis[x]=1,pre[nxt[x]]=pre[x],nxt[pre[x]]=nxt[x];}
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
int main(){
//freopen("text.in","r",stdin);
//freopen("mine.out","w",stdout);
n=read(),m=read(),tot=1;
for(int i=1;i<=n;++i){
x=read();
if((ll)a[tot]*x>=0) a[tot]+=x;
else a[++tot]=x;
}
for(int i=1;i<=tot;++i){
if(a[i]>0) num++,ans+=a[i];
nxt[i]=i+1,pre[i]=i-1,Q.push(node(i,abs(a[i])));
}
while(num>m){
while(vis[Q.top().pos]) Q.pop();
x=Q.top().pos,Q.pop();
if(((pre[x])&&(nxt[x]!=tot+1))||(a[x]>0)){
--num,ans-=abs(a[x]),a[x]+=a[pre[x]]+a[nxt[x]];
Q.push(node(x,abs(a[x]))),del(pre[x]),del(nxt[x]);
}
else del(x);
}cout<<ans<<endl;
}