题目的连接
今天开始学习splay树,如cxlove大神所说,开始主要追随别人的代码,然后写出适合自己的,学习的话可以参考kuangbin的这篇文章,及里面提及的论文
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
const int inf=1<<28;
int pre[maxn],key[maxn],ch[maxn][2],root,tot1;//父亲的节点,键值,左右儿子,根,用于newnode产生节点的计数器
int n;
void NewNode(int &r,int father,int k){//生成新的节点,节点的父亲是father,关键值是key
r=++tot1;
pre[r]=father;
key[r]=k;
ch[r][0]=ch[r][1]=0;//左右儿子开始是空
}
void Rotate(int x,int kind){//kind==0表示左旋(是指x绕父节点左旋),kind==1表示右旋
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 x,int goal){//进行伸展的操作
while(pre[x]!=goal){
if(pre[pre[x]]==goal){
Rotate(x,ch[pre[x]][0]==x);
}
else {
int y=pre[x];
int kind=ch[pre[y]][0]==y;
if(ch[y][kind]==x){//方向不一致,做两次方向不同的旋转
Rotate(x,!kind);
Rotate(x,kind);
}
else {//方向一样,两次相同的旋转
Rotate(y,kind);
Rotate(x,kind);
}
}
}
if(goal==0)root=x;//更新父节点
}
int Insert(int k){//插入一个键值
int r=root;
while(ch[r][key[r]<k]){
if(key[r]==k){//避免重复插入
Splay(r,0);
return 0;
}
r=ch[r][key[r]<k];
}
NewNode(ch[r][key[r]<k],r,k);
Splay(ch[r][key[r]<k],0);
return 1;
}
int get_pre(int x){//找X的前驱
int tmp=ch[x][0];
if(tmp==0)return inf;
while(ch[tmp][1])
tmp=ch[tmp][1];
return key[x]-key[tmp];
}
int get_next(int x){//找X的后继
int tmp=ch[x][1];
if(!tmp) return inf;
while(ch[tmp][0])
tmp=ch[tmp][0];
return key[tmp]-key[x];
}
int main(){
while(scanf("%d",&n)!=EOF){
root=tot1=0;
int ans=0;
for(int i=1;i<=n;i++){
int num;
if(scanf("%d",&num)==EOF) num=0;
if(i==1){
ans+=num;
NewNode(root,0,num);
continue;
}
if(Insert(num)==0)continue;
int a=get_next(root);
int b=get_pre(root);
ans+=min(a,b);
}
printf("%d\n",ans);
}
return 0;
}