51Nod 1052 最大M子段和

6 篇文章 0 订阅
4 篇文章 0 订阅

先将同符号的每一段存下来,并记录前后段标号,将正数段总和记录为tot
开一个最小堆存每一段的abs值。
每次从堆中取出一个x
去掉这一段,并将左右的段与之合并再存入堆中,作为撤销或更改操作。
每次操作都会减少一段,直到段数=k就退出。

#include<cstdio>  
#include<cstdlib>  
#include<cmath>  
#include<algorithm> 
#define C (c=getchar())
using namespace std;  
typedef long long ll;  
inline void read(ll &x)  
{  
    static char c;int b=1;C;  
    for (;!(c>='0'&&c<='9');C) if (c=='-') b=-1;  
    for (x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-'0',C); x*=b;  
}  
const ll N=1000005;
ll n,m,nn,cnt;  
ll a[N],v[N];  
ll pre[N],nxt[N];  
ll ans,tot;  
ll heap[2*N],ma;  
ll bk[2*N];  
inline ll val(ll i) {return abs(v[heap[i]]);}  
inline ll F(ll i) {return (i<<1)==ma?(i<<1):(val(i<<1)<val(i<<1|1)?(i<<1):(i<<1|1));}  
inline ll Swap(ll x,ll y) {swap(heap[x],heap[y]);swap(bk[heap[x]],bk[heap[y]]);}   
inline void down(ll x)  
{ 
    ll i;if (x>ma) return;  
    for (i=x,nn;(i<<1)<=ma&&val(nn=F(i))<val(i);) Swap(i,nn),i=nn;  
}  
inline void up(ll x)  
{  
    ll i;if (x>ma) return;  
    for (i=x;i!=1&&val(i)<val(i>>1);)Swap(i,i>>1),i>>=1;  
}  
inline void det(ll x)  
{  
    Swap(x,ma);   
    ma--;   
    down(x);  
    up(x);  
}  
inline void insert(ll x)  
{  
    heap[++ma]=x;   
    bk[x]=ma;   
    up(ma);  
}  
inline void Solve()  
{  
    ll k,a,b,i;  
    for (i=1;i<=cnt;i++) insert(i);  
    while (tot>m)  
    {  
        k=heap[1];  
        if (pre[k]==-1)  
        {  
            if (v[k]>0) ans-=abs(v[k]),det(1),tot--;  
            else det(1);  
            pre[nxt[k]]=pre[k];  
        }  
        else if (nxt[k]==-1)  
        {  
            if (v[k]>0) ans-=abs(v[k]),det(1),tot--;  
            else det(1);  
            nxt[pre[k]]=nxt[k];   
        }  
        else  
        {  
            ans-=abs(v[k]);  
            a=nxt[k];b=pre[k];  
            pre[k]=pre[b];if (pre[b]!=-1) nxt[pre[b]]=k;  
            nxt[k]=nxt[a];if (nxt[a]!=-1) pre[nxt[a]]=k;  
            v[k]+=v[a]+v[b];v[a]=v[b]=0;  
            down(1),det(bk[a]),det(bk[b]),tot--;  
        }  
    }  
}  
int main()  
{ 
    ll i;read(n),read(m);  
    for (i=1;i<=n;i++)  
    {  
        read(a[++nn]);  
        if (!a[nn]) nn--;  
    }  
    v[++cnt]=a[1];  
    for (i=2;i<=nn;i++)  
    if ((a[i]>0&&a[i-1]>0)||(a[i]<0&&a[i-1]<0)) v[cnt]+=a[i];  
    else v[++cnt]=a[i];  
    for (i=1;i<=cnt;i++)  
    if (v[i]>0) ans+=v[i],tot++;  
    for (i=1;i<=cnt;i++) pre[i]=i-1,nxt[i]=i+1;  
    pre[1]=nxt[cnt]=-1;  
    Solve();  
    printf("%lld\n",ans);  
    return 0;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值