花了一天钻研了splay,然后发现splay没我想象的那么难……以前都是写SBT来着…… 但是splay的速度确实没那么快,但是真的挺好写的~
我写的版本测了这题以后又用了一下别人的splay,发现通过这题大多数splay在750ms上下,我是688ms,说明还算是不错的啦~啦啦啦~
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; struct node { int count, key; node *left_son, *right_son, *father; node():count(0), key(0), left_son(NULL), right_son(NULL), father(NULL){} node(int tmp, node *tmp_node):count(1), key(tmp), left_son(NULL), right_son(NULL), father(tmp_node){} }*proot = new node; //返回a指针的儿子数量 #define RNC(a) ((a)==NULL?0:a->count) void pg(node &t) { cout<<"key = "<<t.key<<" count = "<< t.count <<" " << endl; if (t.father != NULL) cout <<"fa ="<<t.father -> key<<" "; else cout<<"no father "; if (t.left_son != NULL) cout<<"L = "<<t.left_son -> key<<" "; else cout<<"no L "; if (t.right_son != NULL) cout<<"R = "<<t.right_son -> key<<" "; else cout<<"no R "; cout<<endl<<endl; if (t.left_son != NULL) pg(*t.left_son); if (t.right_son != NULL) pg(*t.right_son); } void zig(node &x) //右旋 { node *y =x.father; x.count = RNC(x.left_son) + RNC(x.right_son) + 1 + RNC(y -> right_son) + 1; y -> count = x.count - 1 - RNC(x.left_son); /*重新出x节点,和y节点的儿子数量*/ y -> left_son = x.right_son; if (x.right_son != NULL) x.right_son -> father = y; x.father = y -> father; if (y -> father != NULL) if (y -> father -> left_son == y) y -> father -> left_son = &x; else y -> father -> right_son = &x; y -> father = &x; x.right_son = y; } void zag(node &x) { node *y = x.father; x.count = RNC(x.right_son) + RNC(x.left_son) + 1 + RNC(y -> left_son) + 1; y -> count = x.count - 1 - RNC(x.right_son); y -> right_son = x.left_son; if (x.left_son != NULL) x.left_son -> father = y; x.father = y -> father; if (y -> father != NULL) if (y -> father -> left_son == y) y -> father -> left_son = &x; else y -> father -> right_son = &x; y -> father = &x; x.left_son = y; } void splay(node &x) { node *y; while ((y = x.father) != NULL) { if (y -> father == NULL) { if (&x == y -> left_son) zig(x); else zag(x); break; } if (&x == y -> left_son) { if (y == y -> father -> left_son) { zig(*y); zig(x); }else{ zig(x); zag(x); } }else { if (y == y -> father -> right_son) { zag(*y); zag(x); }else{ zag(x); zig(x); } } } proot = &x; } node* search_node(int valve, node &t = *proot)//搜索valve从t节点开始搜索,找不到,就返回最后找到的位置 { node *x = &t; while (x -> key != valve) { if (valve <= x -> key) { if (x -> left_son == NULL) break; x = x -> left_son; }else{ if (x -> right_son == NULL) break; x = x -> right_son; } } return x; } void insert_node(int valve) { if (!proot -> count) { proot -> count = 1; proot -> key = valve; return; } node *pos(search_node(valve, *proot)); if (valve <= pos -> key) { pos -> left_son = new node(valve, pos); splay(*(pos -> left_son)); }else { pos -> right_son = new node(valve, pos); splay(*(pos -> right_son)); } } /* * 寻找从x节点为根的极大值 * 直接去search_node去找一个极大值,一定找不到,最后找到的那个点,就是最大值 * 把最大值调整到他们通过父亲节点所能追溯到的最早的节点 * PS:为何不直接调整到根?为何总是要调整? 其实都是代码省事的 > 真正的效率…… */ node* exterme_max(node &x) //找从x节点为根的最大值 { node *pos = search_node(0x7fffffff, x); splay(*pos); return pos; } node* exterme_min(node &x) { node *pos = search_node(-0x7fffffff, x); splay(*pos); return pos; } /* * 从整棵树中删除值为valve的节点 * 找到valve,旋转到根 * 如果只有一个节点,那么要特殊判断一下 * 如果根没有左儿子,那么直接让右儿子当根 * 如果有左儿子,那么找到左儿子的最大值,旋转到左儿子的位置上 * 此时,[根的左儿子]一定没有[右儿子](他最大,当然没有右儿子) * 把[根的右儿子],并到[根的左儿子]的右儿子上 */ bool delete_node(int valve) { node *pos = search_node(valve, *proot); if (pos -> key != valve) return false;//根本没有valve 删除失败 splay(*pos); if (proot -> left_son == NULL) { proot = pos -> right_son; delete pos; }else{ proot -> left_son -> father = NULL; proot = exterme_max(*(pos -> left_son)); delete pos; } } /*找前驱 * 先找到valve,把valve旋转到根节点 * 这个时候,根左儿子的最大值,和右儿子最小值,就是valve的前驱。 后继同理 * PS:返回下标,有利于判断是否不存在前驱后继 */ node* find_node_pre(int valve) { node *pos = search_node(valve, *proot); splay(*pos); if (proot -> left_son == NULL) return NULL; return exterme_max(*(proot -> left_son)); } //找后继 node* find_node_next(int valve) { node *pos = search_node(valve, *proot); splay(*pos); if (proot -> right_son == NULL) return NULL; return exterme_min(*(proot -> right_son)); } //查询valve的排名1 ****因为本题没有设计查排名操作,所以查排名操作没有验证对错 /* * 查询valve排名,和其他二叉树方法一样 */ int rank_node(int valve) { int ans = 0; node *x = proot; while (x -> key != valve) { if (valve <= x ->key) { if (x -> left_son == NULL) return -1; x = x -> left_son; }else { if (x -> right_son == NULL) return -1; ans += 1 + RNC(x -> left_son); x = x-> right_son; } } ans += RNC(x -> left_son); return ans; } //查排名操作2 int find_rank_node(int valve) { search_node(valve, *proot); return RNC(proot -> left_son) + 1; } int n, tmp, ans; int main() { ios::sync_with_stdio(false); cin >> n; cin >> tmp; --n; ans = tmp; insert_node(tmp); while (n--) { if ( (cin >> tmp) == NULL) tmp =0; if (search_node(tmp) -> key == tmp) continue; insert_node(tmp); node *pre_node = find_node_pre(tmp); node *next_node = find_node_next(tmp); int a, b; if (pre_node == NULL) a = 0x7fffffff; else a = pre_node -> key; if (next_node == NULL) b = 0x7fffffff; else b = next_node -> key; a = abs(a - tmp); b = abs(b - tmp); ans += min(a, b); } cout << ans << endl; return 0; }
花了时间研究zkw splay tree,也就是自顶向下维护的神奇splay,各种快!而且程序更短功能更好用!!!决定用这个程序当模板了!
强烈推荐,速度快了6倍!
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int maxint = 0x7fffffff; struct node { int key; int size; node *c[2]; node(): key(0), size(0){c[0] = c[1] = this;} node (int KEY_, node *c0, node *c1): key(KEY_){c[0] = c0, c[1] = c1;} node* rz(){return size = c[0] -> size + c[1] -> size + 1, this;} } Tnull, *null = &Tnull; struct splay { node *root; splay() { root = (new node(*null)) -> rz(); root -> key = maxint; } void zig(bool d) { node *t = root -> c[d]; root -> c[d] = null -> c[d]; null -> c[d] = root; root = t; } void zigzig(bool d) { node *t = root -> c[d] -> c[d]; root -> c[d] -> c[d] = null -> c[d]; null -> c[d] = root -> c[d]; root -> c[d] = null -> c[d] -> c[!d]; null -> c[d] -> c[!d] = root -> rz(); root = t; } void finish(bool d) { node *t = null -> c[d], *p = root -> c[!d]; while (t != null) { t = null -> c[d] -> c[d]; null -> c[d] -> c[d] = p; p = null -> c[d] -> rz(); null -> c[d] = t; } root -> c[!d] = p; } void select(int k) { int t; while (1) { bool d = k > (t = root -> c[0] -> size); if (k == t || root -> c[d] == null) break; if (d) k -= t + 1; bool dd = k > (t = root -> c[d] -> c[0] -> size); if (k == t || root -> c[d] -> c[dd] == null){zig(d); break;} if (dd) k -= t + 1; d != dd ? zig(d), zig(dd) : zigzig(d); } finish(0), finish(1); root -> rz(); } void search(int x) { while (1) { bool d = x > root -> key; if (root -> c[d] == null) break; bool dd = x > root -> c[d] -> key; if (root -> c[d] -> c[dd] == null){zig(d);break;} d != dd ? zig(d), zig(dd) : zigzig(d); } finish(0); finish(1); root -> rz(); if (x > root -> key) select(root -> c[0] -> size + 1); } void ins(int x) { search(x); node *oldroot = root; root = new node(x, oldroot -> c[0], oldroot); oldroot -> c[0] = null; oldroot -> rz(); root -> rz(); } void del(int x) { search(x); node *oldroot = root; root = root -> c[1]; select(0); root -> c[0] = oldroot -> c[0]; root -> rz(); delete oldroot; } int sel(int k){return select(k - 1), root -> key;} int ran(int x){return search(x), root -> c[0] -> size + 1;} }sp; int n, tmp, ans; int main() { ios::sync_with_stdio(false); cin >> n; cin >> tmp; --n; ans = tmp; sp.ins(tmp); while (n--) { if ( (cin >> tmp) == NULL) tmp =0; sp.ins(tmp); int rank = sp.ran(tmp); int a = maxint, b = maxint; int t1 = maxint/2, t2 = maxint; if (rank != 1) { t1 = a = sp.sel(rank - 1); a = abs(a - tmp); } t2 = b = sp.sel(rank + 1); // cout<<rank<<" "<<t1<<" "<<t2<<endl; b = abs(b - tmp); ans += min(a, b); } cout << ans << endl; return 0; }