首先!特别强调!这题非常坑!比如第一个点!n=32767,但是只给了你32766个值,最后一个当成0来做!就这问题笔者调了一个小时才发现!而且网上的题解中都没有提到!请各位猿们在写代码时特别注意!!!
今天刚刚学了Splay,就拿这题练手了。
关于Splay,建议大家去看国家集训队论文2004年杨思雨的《伸展树的基本操作与应用》,笔者觉得很适合初学者看。
那么对于本题,目的是对于每个数找出之前的数中和它相差最小的数。
每读入一个数就插入树中,经过Splay操作后查找其前趋和后继并进行比较,计算答案。
讲义中虽然没有提到,但是直接Splay的话要判断是Zig还是Zag,但是Splay操作实际代码实现时非常简洁巧妙,希望各位猿们慢慢的看和领悟,拿着草稿纸自己画画写写,当你像笔者当时一样终于看懂时,你会发现这个方法实在是太巧妙了,平衡树往往一两百行的程序,但是这题笔者只写了70行。
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
using namespace std;
#define read freopen("1588.in","r",stdin)
#define write freopen("1588.out","w",stdout)
#define inf (1<<30)
const int maxn=100005;
int n,k,pre[maxn],data[maxn],ch[maxn][2],tot,ans,root;
//kind==1 Zig kind==0 Zag
void rotate(int x,int kind){
int y=pre[x];
ch[y][!kind]=ch[x][kind];
pre[ch[x][kind]]=y;
if (pre[y])
ch[pre[y]][ch[pre[y]][1]==y]=x;
pre[x]=pre[y];
ch[x][kind]=y;
pre[y]=x;
}
void splay(int t){
while (pre[t]!=0)
if (pre[pre[t]]==0) rotate(t,ch[pre[t]][0]==t);
else{
int y=pre[t];
bool kind=ch[pre[y]][1]==y;
if (ch[y][kind]==t)
rotate(t,!kind),rotate(t,!kind);
else
rotate(t,kind),rotate(t,!kind);
}
root=t;
}
void ins(int k){ //伸展树的查找、插入和查找树是一样的
int now;
for (now=root;ch[now][data[now]<k];now=ch[now][data[now]<k]);
data[++tot]=k;
ch[now][data[now]<k]=tot;
pre[tot]=now;
splay(tot);
}
int get_pre(){
int now=ch[root][0];
if (!now) return inf; //特判是否有左/右子树
for (;ch[now][1];now=ch[now][1]);
return k-data[now];
}
int get_suf(){
int now=ch[root][1];
if (!now) return inf; //同上
for (;ch[now][0];now=ch[now][0]);
return data[now]-k;
}
int main(){
#ifndef ONLINE_JUDGE
read;write;
#endif
cin>>n;tot=0;
for (int i=1;i<=n;i++){
if (scanf("%d",&k)==EOF) k=0;
ins(k);
if (i==1) {ans+=k;continue;}
ans+=min(get_pre(),get_suf());
}
cout<<ans<<endl;
return 0;
}