1. 程式人生 > >51nod 1053 最大M子段和 V2

51nod 1053 最大M子段和 V2

題意就是讓你選擇m段不想交的區間使的和最大,hk比賽的時候遇到的,當時不會做,後來大佬說是上古原題,51nod上找到了,我們首先預處理區間,把區間變成正負相替的區間,然後開始刪區間,使得剩下的區間個數為m個,我們現在有兩個選擇,可以選擇刪一個正數區間,使得兩個負數區間相連,或者刪除一個負數區間,使得相鄰的兩個正數區間相連。每次刪除的時候都要刪除能刪除的裡面的最小值,所以我們可以用一個set維護絕對值,從小到大的刪,用連結串列維護前驅和後驅

#include<bits/stdc++.h>
using namespace std;
using LL = int64_t;
const int maxn=1e5+5;
const LL mod=1e9+7;
const int INF=1e9;
const LL LLINF=1e14;
const double pi=acos(-1.0);
LL a[maxn],pre[maxn],nxt[maxn];
set<pair<LL,int>>st;

void _erase(int x) {
    int l=pre[x],r=nxt[x];
    if(l) nxt[l]=r;
    if(r) pre[r]=l;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int n,m;cin>>n>>m;
    LL sum=0,num=1,cnt=0,res=0;
    for(int i=1;i<=n;i++) {
        LL x;cin>>x;
        if(x>0&&sum<0) {
            a[num++]=sum;
            sum=x;
        }
        else if(x<0&&sum>0) {
            a[num++]=sum;
            sum=x;
        }
        else sum+=x;
    }
    if(sum!=0) a[num++]=sum;
    for(int i=1;i<num;i++) {
        pre[i]=i-1,nxt[i]=i+1;
        st.insert({abs(a[i]),i});
        if(a[i]>0) cnt++,res+=a[i];
    }
    nxt[num-1]=0;
    while(cnt>m) {
        int id=(*st.begin()).second;
        st.erase(st.begin());
        if((a[id]<0&&(pre[id]==0||nxt[id]==0))||a[id]==0) continue;
        st.erase({abs(a[pre[id]]),pre[id]});
        st.erase({abs(a[nxt[id]]),nxt[id]});
        res-=abs(a[id]);
        a[id]=a[id]+a[pre[id]]+a[nxt[id]];
        _erase(pre[id]);
        _erase(nxt[id]);
        st.insert({abs(a[id]),id});
        cnt--;
    }
    cout<<res;
    return 0;
}