[APIO/CTSC 2007]数据备份

[APIO/CTSC 2007]数据备份

真正的贪心好题

一段区间被取后,旁边两端区间不能再取,但我们可以舍弃掉这一段,去取旁边的两段

这样的贪心策略怎么维护呢?

我们用堆维护贪心,每次选择这段区间后,将两边的区间合并成一段,权值是\(w_{i-1}+w_{i+1}-w_i\)

也就是舍弃中间这一段,取两边的

但是这样的做法只能做一次舍弃,事实上我们可能会有多次,然而我们只用推广一下即可

用一个双向链表维护,改变后的权值就是\(w_{pre[i]}+w_{nxt[i]}-w_i\),这对于多个组合同样适用

注意记录这个点是否已经被合并掉了

#include<cstdio> 
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
 
#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
  
char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}
 
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }
 
 
const int N=1e5+10,K=21,INF=1e9+10;
 
int n,k;
int d[N];
struct Node{
    int x,id;
    bool operator < (const Node __) const {
        return x>__.x;
    }
};
priority_queue <Node> Q;
int L[N],R[N];
int ans;
int del[N];
 
void Del(int x) {
    L[R[x]]=L[x];
    R[L[x]]=R[x];
    del[x]=1;
}
 
int main(){
    n=rd(),k=rd();
    rep(i,1,n) d[i]=rd(),L[i]=i-1,R[i]=i+1;
    R[n+1]=n,R[0]=1;
    drep(i,n,2) {
        d[i]-=d[i-1];
        Q.push((Node){d[i],i});
    }
    d[1]=d[n+1]=INF;
    rep(i,1,k) {
        int t=Q.top().id; Q.pop();
        if(del[t]) {
            i--;
            continue;
        }
        ans+=d[t];
        d[t]=d[L[t]]+d[R[t]]-d[t];
        Del(L[t]),Del(R[t]);
        Q.push((Node){d[t],t});
    }
    printf("%d\n",ans);
}
 
posted @ 2019-11-08 17:45  chasedeath  阅读(175)  评论(0编辑  收藏  举报