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 }
愿你在冷铁卷刃之前,得以窥见天光

浙公网安备 33010602011771号