题目大意:
中文题面= =
大致思路:
作为初学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; }