平衡树Splay

LuoguP3369

这道题啊。。。

一开始学习的时候遇到了一篇讲的超级好的博客:rentenglong 的博客

这篇大家一定要去瞅瞅,讲的炒鸡详细(就是代码有点锅。。。)

后来找到一份和TA码风差不多的,才完善了一哈qwq

不过最终还是过了

Code:

  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 }
View Code
posted @ 2019-11-10 15:37  Sun-dial  阅读(158)  评论(0编辑  收藏  举报