『學習筆記』Splay
阿新 • • 發佈:2021-12-24
不打算詳細寫了,強推一波 yyb 神仙的部落格 Splay入門解析【保證讓你看不懂(滑稽)】
(這篇部落格的程式碼完全是按照 yyb 的部落格寫的,並有一些補充,包括 pushup
及查詢第 k 大的整數等等)
這裡列幾個注意事項吧:
- \(Splay\) 過程中,如果 \(x, y\) 為同一種兒子,那麼先旋轉 \(y\),再轉 \(x\),否則旋轉兩次 \(x\)。
- \(pushup\) 不要忘記寫。
emm……別的似乎就沒了。
(以後有時間的話可能會稍微寫的詳細一點,先咕了)
\(Code:\)
(原題是洛谷 P3369,程式碼裡有較為詳細的註釋)
\[\_EOF\_ \]#include <bits/stdc++.h> #define ls(x) t[x].ch[0] #define rs(x) t[x].ch[1] using namespace std; namespace IO{ inline int read(){ int x = 0, f = 1; char ch = getchar(); while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();} while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } template <typename T> inline void write(T x){ if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); } } using namespace IO; const int N = 1e6 + 10; int n; struct splay{ int val, siz, fa, ch[2], cnt; }t[N]; int root, tot; inline void pushup(int x){ t[x].siz = t[ls(x)].siz + t[rs(x)].siz + t[x].cnt; } //旋轉 inline void rotate(int x){ int y = t[x].fa, z = t[y].fa; int k = (rs(y) == x);// 0 是左兒子,1 是右兒子 t[z].ch[(rs(z) == y)] = x;//z 原來的 y 位置變成 x t[x].fa = z; t[y].ch[k] = t[x].ch[k ^ 1];//x 的另一個兒子變成 x 在 y 原本位置上的兒子 t[t[x].ch[k ^ 1]].fa = y;//更新父親 t[x].ch[k ^ 1] = y, t[y].fa = x;//更新 x 和 y 的關係 pushup(y), pushup(x); } //核心 splay inline void splay(int x, int goal){ while(t[x].fa != goal){ int y = t[x].fa, z = t[y].fa; if(z != goal) rotate((ls(z) == y) ^ (ls(y) == x) ? x : y);//splay 雙旋分類討論 rotate(x); } if(!goal) root = x; } //查詢 x 的位置,並 splay 到根 inline void find(int x){ int u = root; if(!u) return; while(t[u].ch[x > t[u].val] && x != t[u].val) u = t[u].ch[x > t[u].val];//小於 -> 往左跳,大於 -> 往右跳 splay(u, 0); } //插入一個新節點 inline void insert(int x){ int u = root, fa = 0; while(u && t[u].val != x) fa = u, u = t[u].ch[x > t[u].val];//找到等於 x 的點 if(u) t[u].cnt++;//如果有,計數 +1 else{ u = ++tot;//沒有,新建節點 if(fa) t[fa].ch[x > t[fa].val] = u; t[u].ch[0] = t[u].ch[1] = 0; t[u].siz = t[u].cnt = 1, t[u].val = x, t[u].fa = fa; } splay(u, 0); } //查詢 前驅/後繼 inline int check(int x, int type){//type = 0 前驅,1 後繼 find(x); int u = root;//根節點,此時 x 的父節點(存在的話)就是根節點 if((t[u].val > x && type) || (t[u].val < x && !type)) return u; u = t[u].ch[type]; while(t[u].ch[type ^ 1]) u = t[u].ch[type ^ 1];//反著跳 return u; } //刪除 inline void remove(int x){ int lst = check(x, 0), nxt = check(x, 1);//查 前驅/後繼 splay(lst, 0), splay(nxt, lst);//前驅 splay 到根,後繼 splay 到前驅,x 是後繼的左子節點,且是葉節點 int del = ls(nxt); if(t[del].cnt > 1) t[del].cnt--, splay(del, 0);//如果個數 > 1, 直接-- else t[nxt].ch[0] = 0;//只有一個,刪掉 nxt 的左兒子 pushup(nxt), pushup(lst); } //查詢第 k 大 inline int get_val(int x, int k){ if(k <= t[ls(x)].siz) return get_val(ls(x), k); else if(k > t[ls(x)].siz + t[x].cnt) return get_val(rs(x), k - t[ls(x)].siz - t[x].cnt); else return t[x].val; } int main(){ n = read(); insert(-2147483647), insert(2147483647); for(int i = 1; i <= n; ++i){ int op = read(), x = read(); if(op == 1) insert(x); else if(op == 2) remove(x); else if(op == 3) find(x), write(t[ls(root)].siz), puts(""); else if(op == 4) write(get_val(root, x + 1)), puts(""); else if(op == 5) write(t[check(x, 0)].val), puts(""); else write(t[check(x, 1)].val), puts(""); } return 0; }
本文來自部落格園,作者:xixike,轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15729172.html