splay瞎存

  1 #define WMX aiaiaiai~~~
  2 #include <bits/stdc++.h>
  3 #define LL long long 
  4 #define Re register int
  5 #define St short
  6 #define ki cout << endl 
  7 #define fk cout << " ";
  8 #define LD double
  9 #define frein(x) freopen(#x ".in", "r", stdin);
 10 #define freout(x) freopen(#x ".out", "w", stdout);
 11 #define debug cout << " kkkkkkkx " << endl; 
 12 #define fr(i, j, k) for(Re i = j; i <= k; i ++)
 13 #define fp(i, j, k) for(Re i = j; i >= k; i --)
 14 
 15 
 16 //冗长的缺省源 
 17 using namespace std;
 18 
 19 
 20 
 21 
 22 namespace kiritokazuto {
 23     auto read = []() {
 24         LL x = 0;
 25         int f = 1;
 26         char c;
 27         while(!isdigit(c = getchar())) {if(c == '-')f = -1;}
 28         do{x = (x << 1) + (x << 3) + (c ^ 48);}while(isdigit(c = getchar()));
 29         return x * f ;
 30     };
 31     template <typename T> inline void write(T x) {
 32         if(x < 0)putchar('-'), x = -x;
 33         if(x > 9)write(x / 10);putchar(x % 10 | '0');
 34     }
 35 }
 36 using namespace kiritokazuto;
 37 const int maxn = 1e6 + 10;
 38 int tot = 0;//记录节点个数
 39 int rt;//根节点编号 
 40 int son[maxn][2];//0为左儿子,1为右儿子 
 41 int n;
 42 int ans;
 43 //来自冰原骆驼的神奇三招
 44 /*
 45 splay
 46 1.我爹是我爷爷的x儿子,我就是我爷爷的x儿子
 47 2.我是我爹的x儿子,我x ^ 1儿子就是我爹的x儿子
 48 3.我是我爹的x儿子,我爹就是我x ^ 1儿子 
 49 */ 
 50 
 51 struct BST{ 
 52     int cnt;//节点出现次数 
 53     int sz;//子树大小 
 54     int val;//节点权值、 
 55     int pre;//
 56     #define pre(x) wmx[x].pre
 57     #define val(x) wmx[x].val
 58     #define sz(x) wmx[x].sz
 59     #define cnt(x) wmx[x].cnt 
 60     #define lsp(x) son[x][0]
 61     #define rsp(x) son[x][1]
 62 }wmx[maxn];
 63 //inline太多不太好 
 64 int get(int x) {return x == son[pre(x)][1];}//判断x是哪个儿子,如果返回1为右儿子,返回0为左儿子 
 65 inline void pushup(int x) {
 66     sz(x) = sz(lsp(x)) + sz(rsp(x)) + cnt(x);
 67 }
 68 void clear(int x) {
 69     lsp(x) = rsp(x) = pre(x) = val(x) = sz(x) = cnt(x) = 0;
 70 }
 71 
 72 inline void rotate(int x) {//旋转 
 73     int y = pre(x), z = pre(y), sonk = get(x);
 74     son[y][sonk] = son[x][sonk ^ 1];//因为是get和异或,所以做完之后不论是左旋还是右旋,都上去了,具体操作就是看那三句话 
 75     if(son[x][sonk ^ 1])pre(son[x][sonk ^ 1]) = y;
 76     son[x][sonk ^ 1] = y;
 77     pre(y) = x;
 78     pre(x) = z;
 79     if(z)son[z][y == rsp(z)] = x;
 80     pushup(y);
 81     pushup(x); 
 82 }
 83 /*
 84 注意,这里的对x左旋或者右旋,顺时针右旋,逆时针左旋 
 85 如果x的父亲是根节点,直接将x左旋或右旋
 86 如果x的父亲不是根节点,且x和父亲的儿子类型相同,首先将其父亲左旋或右旋,然后将x右旋或左旋
 87 如果x的父亲不是根节点,且x和父亲的儿子类型不同,将x左旋再右旋、或者右旋再左旋
 88 */
 89 
 90 inline void splay(int x) {
 91     for(Re p = pre(x); p = pre(x), p; rotate(x)) 
 92         if(pre(p)) rotate(get(x) == get(p) ? p : x);
 93     rt = x;
 94 }
 95 /*
 96 假设插入k 
 97 如果树空了,则直接插入根并退出。
 98 如果当前节点的权值等于k则增加当前节点的大小并更新节点和父亲的信息,将当前节点进行 Splay 操作。
 99 否则按照二叉查找树的性质向下找,找到空节点就插入即可(请不要忘记 Splay 操作)。
100 */
101 inline void insert(int k) {
102     if(!rt) {//第一个 
103         val(++tot) = k;
104         cnt(tot)++;
105         rt = tot;
106         pushup(rt);
107         return;
108     }
109     int tmp = rt, p = 0;
110     while(1) {
111         if(val(tmp) == k) {
112             cnt(tmp)++;
113             pushup(tmp);
114             pushup(p);
115             splay(tmp);
116             break;
117         }
118         p = tmp;
119         tmp = son[tmp][val(tmp) < k];
120         if(!tmp) {
121             val(++tot) = k;
122             cnt(tot)++;
123             pre(tot) = p;
124             son[p][val(p) < k] = tot;
125             pushup(tot);
126             pushup(p);
127             splay(tot);
128             break;
129         }
130     } 
131 } 
132 /*
133 如果x比当前节点的权值小,向其左子树查找。
134 如果x比当前节点的权值大,将答案加上左子树(size)和当前节点(cnt)的大小,向其右子树查找。
135 如果x与当前节点的权值相同,将答案加1并返回。
136 注意最后需要进行 Splay 操作。
137 */
138 int rk(int k) {
139     int res = 0, tmp = rt;
140     while(1) {
141         if(k < val(tmp)) {
142             tmp = lsp(tmp);
143         }else {
144             res += sz(lsp(tmp));
145             if(k == val(tmp)) {
146                 splay(tmp);
147                 return res + 1;
148             }
149             res += cnt(tmp);
150             tmp = rsp(tmp);
151         }
152     }
153 } 
154 
155 /*
156 设k为剩余排名
157 如果左子树非空且剩余排名k不大于左子树的大小sz ,那么向左子树查找。
158 否则将k减去左子树的和根的大小。如果此时k的值小于等于0,则返回根节点的权值,否则继续向右子树查找。
159 */
160 
161 int kth(int k) {
162     int tmp = rt;
163     while(1) {
164         if(lsp(tmp) && k <= sz(lsp(tmp))) {
165             tmp = lsp(tmp);
166         }else {
167             k -= cnt(tmp) + sz(lsp(tmp));
168             if(k <= 0) {
169                 splay(tmp);
170                 return val(tmp);
171             }
172             tmp = rsp(tmp);
173         }
174     }
175 }
176 
177 /*
178 前驱定义为小于x的最大的数,那么查询前驱可以转化为:
179 将x插入(此时x已经在根的位置了)
180 前驱即为x的左子树中最右边的节点,最后将x删除即可。
181 */
182 int getp() {
183     int tmp = lsp(rt);
184     if(!tmp)return tmp;
185     while(rsp(tmp)) tmp = rsp(tmp);
186     splay(tmp);
187     return tmp; 
188 }
189 /*
190 后继定义为大于x的最小的数,查询方法和前驱类似
191 x的右子树中最左边的节点。
192 */
193 int getnxt() {
194     int tmp = rsp(rt);
195     if(!tmp) return tmp;
196     while(lsp(tmp))tmp = lsp(tmp);
197     splay(tmp);
198     return tmp;
199 }
200 /*
201 合并两棵 Splay 树,设两棵树的根节点分别为x和y,那么我们要求x树中的最大值小于y树中的最小值。合并操作如下:
202 如果x和y其中之一或两者都为空树,直接返回不为空的那一棵树的根节点或空树。
203 否则将x树中的最大值splay到根,然后把它的右子树设置为y并更新节点的信息,然后返回这个节点。
204 */
205 
206 
207 /*
208 删除操作也是一个比较复杂的操作,具体步骤如下:
209 首先将x旋转到根的位置。
210 如果cnt[x] > 1(有不止一个x),那么将 cnt[x]减1并退出。
211 否则,合并它的左右两棵子树即可。
212 */
213 void del(int k) {
214     rk(k);
215     if(cnt(rt) > 1) {
216         cnt(rt)--;
217         pushup(rt);
218         return;
219     }
220     if(!lsp(rt) && !rsp(rt)) {
221         clear(rt);
222         rt = 0;
223         return;
224     }
225     if(!lsp(rt)) {
226         int tmp = rt;
227         rt = rsp(rt);
228         pre(rt) = 0;
229         clear(tmp);
230         return;
231     }
232     if(!rsp(rt)) {
233         int tmp = rt;
234         rt = lsp(rt);
235         pre(rt) = 0;
236         clear(tmp);
237         return;
238     }
239     int tmp = rt, x = getp();
240     pre(rsp(tmp)) = x;
241     rsp(x) = rsp(tmp);
242     clear(tmp);
243     pushup(rt);
244     
245 }
246 
247 signed main () {
248      n = read();
249      int tp;
250      int opt1, opt2;
251      
252     fr(i, 1, n){
253         tp = read();
254         insert(tp);
255         if(i == 1){
256             ans += tp;
257             continue;
258         }
259         if(cnt(rt) == 1){
260             opt1 = getp();
261             opt1 = (opt1 == 0) ? -0x7ffffff : val(opt1);
262             rk(tp);
263             opt2 = getnxt();
264             opt2 = (opt2 == 0) ? 0x7ffffff : val(opt2);
265             ans += min(tp - opt1, opt2 - tp);  
266         //    printf(" kk kk kk k%d \n",ans); 
267         }
268     }
269 //    printf("%d",ans); 
270 }

 

posted @ 2022-06-20 15:43  kiritokazuto  阅读(82)  评论(2)    收藏  举报