题目大意:
中文题面= =
大致思路:
作为初学Splay的第一道练习题.....
使用到的操作:
1.在原序列末尾插入新元素, 键值不出现重复
2. 寻找比某个键值小或者大的最近的键值, 通过Splay转为求根节点的前驱和后继
代码如下:
Result : Accepted Memory : 2836 KB Time : 140 ms
/*
* Author: Gatevin
* Created Time: 2015/8/21 15:30:30
* File Name: Sakura_Chiyo.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
struct Splay_Tree
{
#define maxn 100010
int pre[maxn];//u父亲节点是pre[u]
int key[maxn];//键值
int next[maxn][2];//next[u][0]是结点u的左子树, next[u][1]是u的右子树
int root;//当前的根节点
int tot;//总的结点数量
void newnode(int &r, int father, int value)//新建结点, 父亲是father, 键值为value
{
r = ++tot;
pre[r] = father;
key[r] = value;
next[r][0] = next[r][1] = 0;//新节点r的左右子树为空
return;
}
//旋转, kind = 1表示右旋, kind = 0表示左旋
void Rotate(int x, int kind)
{
int y = pre[x];
next[y][!kind] = next[x][kind];
pre[next[x][kind]] = y;
if(pre[y])
next[pre[y]][next[pre[y]][1] == y] = x;
pre[x] = pre[y];
next[x][kind] = y;
pre[y] = x;
return;
}
//Splay伸展调整, 将根为r的子树调整为goal, (将r所在子树调整为goal的左或者右子树
void Splay(int r, int goal)//goal为0会旋转至根节点, pre[root] = 0
{
//旋转会使得r的父亲是goal
while(pre[r] != goal)
{
if(pre[pre[r]] == goal)//只需要进行一次Zig或者Zag操作即可
Rotate(r, next[pre[r]][0] == r);//r是左子树就右旋, 否则左旋
else
{
int y = pre[r];
int kind = next[pre[y]][0] == y;//y是否是左子树
if(next[y][kind] == r)//y左r右或者 y右r做, 说明要进行Zig-Zag或者Zag-Zig操作
{
Rotate(r, !kind);
Rotate(r, kind);
}
else//y, r都是左子树或者都是右子树, 进行Zig-Zig或者Zag-Zag操作
{
Rotate(y, kind);
Rotate(r, kind);
}
}
}
if(goal == 0) root = r;
}
int insert(int k)//插入一个键值k
{
int r = root;
while(next[r][key[r] < k])
{
if(key[r] == k)//重复元素, 不插入
{
Splay(r, 0);//将r旋转至根节点
return 0;//插入失败
}
r = next[r][key[r] < k];
}
newnode(next[r][k > key[r]], r, k);
Splay(next[r][k > key[r]], 0);//将新插入的结点旋转至根
return 1;//插入成功
}
//寻找结点编号为x的结点的前驱结点, 即左子树的最右结点
int getPre(int x)//返回结点编号, -1表示没有
{
int tmp = next[x][0];
if(tmp == 0) return -1;
while(next[tmp][1])
tmp = next[tmp][1];
return tmp;//
}
//寻找结点编号为x的结点的后继结点, 即右子树的最左结点
int getNext(int x)//返回结点编号, 编号为-1表示没有
{
int tmp = next[x][1];
if(tmp == 0) return -1;
while(next[tmp][0])
tmp = next[tmp][0];
return tmp;
}
};
Splay_Tree tree;
int n;
const int inf = 1e9 + 7;
int main()
{
while(~scanf("%d", &n))
{
tree.root = tree.tot = 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;
tree.newnode(tree.root, 0, num);
continue;
}
if(tree.insert(num) == 0) continue;//重复值
//由于进行了insert操作num一定在根的位置
int tmp = inf;
int a = tree.getPre(tree.root);
int b = tree.getNext(tree.root);
if(a != -1) tmp = min(tmp, tree.key[tree.root] - tree.key[a]);
if(b != -1) tmp = min(tmp, tree.key[b] - tree.key[tree.root]);
ans += tmp;
}
printf("%d\n", ans);
}
return 0;
}