平衡树Splay
这道题啊。。。
一开始学习的时候遇到了一篇讲的超级好的博客:rentenglong 的博客
这篇大家一定要去瞅瞅,讲的炒鸡详细(就是代码有点锅。。。)
后来找到一份和TA码风差不多的,才完善了一哈qwq
不过最终还是过了
Code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 #define root e[0].son[1] 3 using namespace std; 4 int read() { 5 int re = 0, f = 1; 6 char ch = getchar(); 7 while (ch < '0' || ch > '9') {if (ch == '-') f = -f; ch = getchar();} 8 while ('0' <= ch && ch <= '9') {re = re * 10 + ch - '0'; ch = getchar();} 9 return re * f; 10 } 11 const int N = 1e5 + 3; 12 const int INF = 1e7 + 3; 13 int n, cnt; 14 struct node{ 15 int v, father;//储存键值,父亲节点 16 int son[2];//存储左右孩子,son[0]为左,son[1]为右 17 int sum;//存储这个节点子树共有多少 元素 (元素包括sum + recy) 18 int recy;//存储相同键值重复多少次 19 }e[N]; 20 void update(int x) {//合并 21 e[x].sum = e[e[x].son[0]].sum + e[e[x].son[1]].sum + e[x].recy; 22 } 23 int identify(int x) {//确定当前节点与父亲节点的关系 24 return e[e[x].father].son[0] == x ? 0 : 1; 25 } 26 void connect(int x, int fa, int son) {//连接两个节点 27 e[x].father = fa; 28 e[fa].son[son] = x; 29 } 30 void rotate(int x) {//核心函数之一,旋转 31 int y = e[x].father; 32 int mroot = e[y].father; 33 int mrootson = identify(y); 34 int yson = identify(x); 35 int B = e[x].son[yson ^ 1]; 36 connect(B, y, yson); 37 connect(y, x, (yson ^ 1)); 38 connect(x, mroot, mrootson); 39 update(y);//y在上,先更新y 40 update(x); 41 } 42 void splay(int at, int to) {//核心函数 43 int tofa = e[to].father; 44 while (e[at].father != tofa) { 45 int fa = e[at].father; 46 int gfa = e[fa].father; 47 if (gfa != tofa) { 48 if (identify(at) != identify(fa)) { 49 rotate(at);//当自己的父亲与爷爷不在一条直线上时,先旋转自己 50 } else { 51 rotate(fa);//当自己的父亲与爷爷在一条直线上时,先旋转父亲 52 } 53 } 54 rotate(at); 55 } 56 if (to == root) root = at; 57 } 58 int newpoint(int v, int fa) {//添加新节点 59 e[++cnt].father = fa; 60 e[cnt].v = v; 61 e[cnt].sum = e[cnt].recy = 1; 62 return cnt; 63 } 64 void Insert(int x) { 65 int now = root; 66 if(!root){//特判没根的情况 67 newpoint(x, 0); 68 root = cnt; 69 } else { 70 while (true) { 71 e[now].sum++;//经过的点子树中元素都要++ 72 if (e[now].v == x) { 73 e[now].recy++;//重复 74 splay(now, root); 75 return; 76 } 77 int next = x < e[now].v ? 0 : 1;//判断该走左孩子还是右孩子 78 if (!e[now].son[next]) { 79 int add = newpoint(x, now); 80 e[now].son[next] = add; 81 splay(add, root); 82 return; 83 } 84 now = e[now].son[next]; 85 } 86 } 87 } 88 int find(int v) {//查询键值为v的点的位置 89 int now = root; 90 while (true) { 91 if (e[now].v == v) { 92 splay(now, root);//划重点,下面有用 93 return now; 94 } 95 int next = v < e[now].v ? 0 : 1; 96 if (!e[now].son[next]) { 97 return 0; 98 } 99 now = e[now].son[next]; 100 } 101 } 102 void delet(int x) {//删除节点 103 int now = find(x);//注意find()函数中已经将x转为根节点 104 if (!now) { 105 return; 106 } 107 if (e[now].recy > 1) {//如果有重复,直接减去即可 108 e[now].recy--; 109 e[now].sum--; 110 } else { 111 if (!e[now].son[0] && !e[now].son[1]) {//如果没有左右孩子,说明就一个点 112 root = 0; 113 } else if (!e[now].son[0]) {//如果没有左孩子,直接将右孩子提为根节点 114 root = e[now].son[1]; 115 e[root].father = 0; 116 } else {//左右孩子都有,先将左子树中最大的节点转到左孩子的位置,然后提为根节点,把右孩子连到该节点之下 117 int lef = e[now].son[0]; 118 while (e[lef].son[1]) {//找左子树中最大的节点 119 lef = e[lef].son[1]; 120 } 121 splay(lef, e[now].son[0]); 122 connect(e[now].son[1], lef, 1); 123 connect(lef, 0, 1); 124 update(lef); 125 } 126 } 127 } 128 int _rank(int v) { 129 int now = find(v);//因为find()已经将v转为根节点,所以直接求即可 130 return e[e[now].son[0]].sum + 1; 131 } 132 int arank(int x) { 133 int now = root; 134 while (true) { 135 int used = e[now].sum - e[e[now].son[1]].sum; 136 //如果x大于左子树元素,并且小于等于当前点的左子树的sum加上本节点的recy的值,那么当前的点就是要找的点 137 //因为此时说明该x是当前节点重复值中的一个 138 if (e[e[now].son[0]].sum < x && x <= used) { 139 splay(now, root); 140 return e[now].v; 141 } 142 if (x < used) {//小于则向左子树寻找 143 now = e[now].son[0]; 144 } else {//大于则减去该值,再向右子树寻找 145 x -= used; 146 now = e[now].son[1]; 147 } 148 } 149 } 150 int lower(int v) {//前驱 151 int now = root; 152 int ans = -INF; 153 while (now) { 154 if (e[now].v < v && e[now].v > ans) { 155 ans = e[now].v; 156 } 157 if (v > e[now].v) { 158 now = e[now].son[1]; 159 } else { 160 now = e[now].son[0]; 161 } 162 } 163 return ans; 164 } 165 int upper(int v) {//后继 166 int now = root; 167 int ans = INF; 168 while(now) { 169 if (e[now].v > v && e[now].v < ans) { 170 ans = e[now].v; 171 } 172 if (v < e[now].v) { 173 now = e[now].son[0]; 174 } else { 175 now = e[now].son[1]; 176 } 177 } 178 return ans; 179 } 180 int main () { 181 n = read(); 182 while (n--) { 183 int o = read(); 184 int x = read(); 185 if (o == 1) { 186 Insert(x); 187 } else if (o == 2) { 188 delet(x); 189 } else if (o == 3) { 190 printf("%d\n", _rank(x)); 191 } else if (o == 4) { 192 printf("%d\n", arank(x)); 193 } else if (o == 5) { 194 printf("%d\n", lower(x)); 195 } else if (o == 6) { 196 printf("%d\n", upper(x)); 197 } 198 } 199 return 0; 200 }