题解 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;
}
posted @ 2018-04-14 19:43  Rye_Catcher  阅读(214)  评论(0编辑  收藏  举报