[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);
}