bzoj2288 生日礼物

传送门
把同号的连续一段合并起来。
那么现在一定是正负交替出现。
令正的总和为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值