题解 P3620 【[APIO/CTSC 2007]数据备份】
-
UPDATE
LeTex好象又挂了
-
题目链接:
https://www.luogu.org/problemnew/show/P3620
https://www.lydsy.com/JudgeOnline/problem.php?id=1150 -
思路(来自《算法竞赛进阶指南》):
容易知道,最优解中配对的楼肯定是相邻的,于是我们把所有相邻楼之间的距离\(D_1\),\(D_2\),\(D_3\)...\(D_n\)记录下来,放进一个堆里。
很明显,每次都取堆中的最小值是不正确的。那么这就有个很妙的思路:假设\(D_i\)是最小值,那么我们就取出\(D_i\),同时取出\(D_{i-1}\)和\(D_{i+1}\),然后再把一个一个值\(D_{i+1}\)+\(D_{i-1}\)-\(D_i\)的数放进堆,如果下一步这个新节点是最小值,很明显这是最优解。
- 难点1:
删了\(D_{i+1}\),\(D_{i-1}\)和\(D_{i}\)后插入一个新节点,那它的前驱和后继怎么确定呢?最简单的方式当然就是用链表。
-
难点2:
我们要让堆和链表建立一个映射关系,怎么搞呢???我就在这里卡了好久,其实我们可以用反函数的思想。用一个数组v[]记录外面链表数组在堆中的下标,这样映射就建立了(其实这应该蛮好想的,我还是太弱了)
-
代码:
/*By Rye_Catcher*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#define ll long long
using namespace std;
const int maxn=100005;
int a[maxn],pre[maxn],ne[maxn],v[maxn];
struct Small_Heap{
int heap[maxn],n;
inline void up(int s){
int fa=s>>1;
while(s>1){
if(a[heap[s]]<a[heap[fa]]){
swap(heap[s],heap[fa]);
swap(v[heap[s]],v[heap[fa]]);
s=fa;fa=s>>1;
}
else break;
}
}
inline void insert(int k){
heap[++n]=k;
v[k]=n;
up(n);
}
inline void down(int fa){
int s=fa<<1;
while(s<=n){
if(a[heap[s]]>a[heap[s+1]]&&s<n)s++;
if(a[heap[s]]<a[heap[fa]]){
swap(heap[s],heap[fa]);
swap(v[heap[s]],v[heap[fa]]);
fa=s,s=fa<<1;
}
else break;
}
}
inline void sub(int k){
heap[v[k]]=heap[n];
v[heap[n]]=v[k];
n--;
up(v[k]),down(v[k]);
}
}poi;
int n,k;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;
return ;
}
int main(){
int la,x;
ll ans=0;
read(n),read(k);
read(la);
for(int i=2;i<=n;i++){
read(x);
a[i-1]=x-la;
poi.insert(i-1);
ne[i-1]=i,pre[i-1]=i-2;
la=x;
}
for(register int i=1;i<=k;i++){
x=poi.heap[1];ans+=a[x];
if(pre[x]==0)
{
poi.sub(x),poi.sub(ne[x]);
pre[ne[ne[x]]]=0;
}
else if(ne[x]==n)
{
poi.sub(x),poi.sub(pre[x]);
ne[pre[pre[x]]]=n;
}
else {
poi.sub(x);//poi.extract();
poi.sub(pre[x]),poi.sub(ne[x]);
a[x]=a[pre[x]]+a[ne[x]]-a[x];
poi.insert(x);
pre[x]=pre[pre[x]],ne[pre[x]]=x;
ne[x]=ne[ne[x]],pre[ne[x]]=x;
}
}
cout<<ans<<endl;
return 0;
}