【模板】Splay(伸展树)普通平衡树(数据加强版)/洛谷P6136

1|0题目链接

https://www.luogu.com.cn/problem/P6136

2|0题目大意

需要写一种数据结构,来维护一些非负整数( int 范围内)的升序序列,其中需要提供以下操作:

  1. 插入一个整数 x
  2. 删除一个整数 x (若有多个相同的数,只删除一个)。
  3. 查询整数 x 的排名(排名定义为比当前数小的数的个数 +1 )。
  4. 查询排名为 x 的数(如果不存在,则认为是排名小于 x 的最大数。保证 x 不会超过当前数据结构中数的总数)。
  5. x 的前驱(前驱定义为小于 x ,且最大的数)。
  6. x 的后继(后继定义为大于 x ,且最小的数)。

其中,初始序列大小为 n ,询问/操作总数为 m ,并要求 强制在线
数据范围 n105,m106 , 单点时限 3s

3|0题目解析

要求总复杂度为 O(NlogN) 级别,可以考虑伸展树( Splay ),对单个节点的查询、修改都维持在 O(logN) 级别。

值得注意的是,这里代码中在数据集内多加入了一大( INF )一小( 1 )两个虚设端点作为平衡树的上下限( front,back ),这样在实现查找、插入时可以避免越界,并能减少对于某些情况的特殊判断。

4|0参考代码

#include <bits/stdc++.h> #define N 1200005 #define EMPTY 0 #define INF INT_MAX #define ll long long using namespace std; struct Tree{ int data, dataMin, dataMax, size, fa, child[2]; } t[N]; //其中data, fa, child为节点的基本属性 int cnt, root, front, back; vector <int> dataset, nodeBin; inline void read(int &s) { //快读,支持int s = 0; int tt = 1, k = getchar(); for (; k < '0' || k > '9'; k = getchar()) if (k == '-') tt = -1;//判断该数正负 for (; k >= '0' && k <= '9'; k = getchar()) s = s * 10+(k ^ 48);//^48相当于-‘0’,较快。 s *= tt; } inline void write(ll s) { //快写,支持int和long long int tt = 0, a[40]; if (s < 0) putchar('-'), s = -s; do { a[++tt] = s % 10; } while (s /= 10);//用do while就不用特判一个0 while(tt) putchar(48+a[tt--]); } inline int checkSize(int x) { return (x == EMPTY) ? 0 : t[x].size;} inline int checkDataMin(int x) { return (x == EMPTY) ? INF : t[x].dataMin;} inline int checkDataMax(int x) { return (x == EMPTY) ? -INF : t[x].dataMax;} inline void updNode(int x) { t[x].size = checkSize(t[x].child[0]) + checkSize(t[x].child[1]) + 1; t[x].dataMin = min(t[x].data, min(checkDataMin(t[x].child[0]), checkDataMin(t[x].child[1]))); t[x].dataMax = max(t[x].data, max(checkDataMax(t[x].child[0]), checkDataMax(t[x].child[1]))); } void rotate(int x, int o) { int y = t[x].fa; if (!y) return; int z = t[y].fa; t[y].child[o^1] = t[x].child[o]; if (t[x].child[o] != EMPTY) t[t[x].child[o]].fa = y; t[x].fa = z; if (z != EMPTY) { if (t[z].child[0] == y) t[z].child[0] = x; else t[z].child[1] = x; } t[x].child[o] = y; t[y].fa = x; updNode(y); updNode(x); } void splay(int x) { if (x == EMPTY) return; int y; while (t[x].fa != EMPTY) { y = t[x].fa; if (t[y].fa == EMPTY) //旋转后为根节点 { if (t[y].child[0] == x) rotate(x, 1); else rotate(x, 0); break; } else { if (t[t[y].fa].child[1] == y) { if (t[y].child[0] == x) rotate(x, 1), rotate(x, 0); else rotate(y, 0), rotate(x, 0); } else { if (t[y].child[1] == x) rotate(x, 0), rotate(x, 1); else rotate(y, 1), rotate(x, 1); } } } root = x; } inline int mininum(int x) { //找x的子树中序号最小的 while (t[x].child[0] != EMPTY) x = t[x].child[0]; return x; } inline int maxinum(int x) { //找x的子树中序号最大的 while (t[x].child[1] != EMPTY) x = t[x].child[1]; return x; } inline int succ(int x) { //找x的后继 splay(x); if (t[x].child[1] == EMPTY) return EMPTY; return mininum(t[x].child[1]); } inline int prec(int x) { //找x的前驱 splay(x); if (t[x].child[0] == EMPTY) return EMPTY; return maxinum(t[x].child[0]); } int createNode(int data) //新建节点,存放data(优先取用废弃内存池) { if (nodeBin.empty()) { t[++cnt] = (Tree){data, data, data, 1, EMPTY, EMPTY, EMPTY}; return cnt; } int x = nodeBin.back(); t[x] = (Tree){data, data, data, 1, EMPTY, EMPTY, EMPTY}; nodeBin.pop_back(); return x; } int findKth(int x, int k) //找序号为k的节点 { while (true) { if (x == EMPTY) return EMPTY; int lc = checkSize(t[x].child[0]); if (k <= lc) x = t[x].child[0]; else { if (k == lc+1) return x; else {x = t[x].child[1], k -= lc+1;} } } } inline int getKth(int x) { //找节点x的序号k splay(x); return checkSize(t[x].child[0]) + 1; } void insertKth(int x, int k) //将单节点x插入树中的序号k的位置 { if (!root) {root = x; return;} if (k <= 0 || k > t[root].size+1) return; if (k == 1) { int y = mininum(root); if (y == EMPTY) return; splay(y); t[y].child[0] = x; t[x].fa = y; updNode(y); return; } int y = findKth(root, k-1); if (y == EMPTY) return; splay(y); t[x].child[1] = t[y].child[1]; if (t[y].child[1] != EMPTY) t[t[y].child[1]].fa = x; t[y].child[1] = EMPTY; t[x].child[0] = y; t[y].fa= x; root = x; updNode(y); updNode(x); } void deleteKth(int k) //删除树上序号为k的节点 { if (!root) {return;} if (k <= 0 || k > t[root].size) return; if (k == 1) { int y = mininum(root); if (y == EMPTY) return; nodeBin.push_back(y); splay(y); if (t[y].child[1] != EMPTY) t[t[y].child[1]].fa = EMPTY, root = t[y].child[1]; else root = 0; return; } int y = findKth(root, k); if (y == EMPTY) return; splay(y); nodeBin.push_back(y); int z = prec(y); t[z].child[1] = t[y].child[1]; if (t[y].child[1] != EMPTY) t[t[y].child[1]].fa = z; t[t[y].child[0]].fa = EMPTY; root = t[y].child[0]; while (z != EMPTY) { updNode(z); z = t[z].fa; } } int buildTree(int L, int R, int fa) //建树,data取用dataset[L]~[R],L>0 { if (L > R) return EMPTY; if (L == R) { t[L] = (Tree){dataset[L], dataset[L], dataset[L], 1, fa, EMPTY, EMPTY}; return L; } int mid = (L+R)/2; t[mid] = (Tree){dataset[mid], 0, 0, 0, fa, EMPTY, EMPTY}; t[mid].child[0] = buildTree(L, mid-1, mid); t[mid].child[1] = buildTree(mid+1, R, mid); updNode(mid); return mid; } inline void clearAll() { //清空全部 cnt = 0; dataset.clear(); nodeBin.clear(); root = 0; } int findDataLeq(int x, int data) //找到x的子树上<=Data的最大序号的节点 { int a = EMPTY; if (x == EMPTY) return EMPTY; if (data < t[x].dataMin) return EMPTY; if (t[x].child[1] != EMPTY) { if (data >= checkDataMin(t[x].child[1])) { a = findDataLeq(t[x].child[1], data); } } if (a != EMPTY) return a; if (t[x].data <= data) return x; if (t[x].child[0] != EMPTY) { if (data >= checkDataMin(t[x].child[0])) { a = findDataLeq(t[x].child[0], data); } } if (a != EMPTY) return a; return EMPTY; } int findDataGeq(int x, int data) //找到x的子树上>=Data的最小序号的节点 { int a = EMPTY; if (x == EMPTY) return EMPTY; if (data > t[x].dataMax) return EMPTY; if (t[x].child[0] != EMPTY) { if (data <= checkDataMax(t[x].child[0])) { a = findDataGeq(t[x].child[0], data); } } if (a != EMPTY) return a; if (t[x].data >= data) return x; if (t[x].child[1] != EMPTY) { if (data <= checkDataMax(t[x].child[1])) { a = findDataGeq(t[x].child[1], data); } } if (a != EMPTY) return a; return EMPTY; } inline void printTree() { //将树上每个节点data按序输出 int x = mininum(root); while (x != EMPTY) { write(t[x].data); x = succ(x); if (x != EMPTY) putchar(' '); } putchar('\n'); } int main() { int n, m, last = 0, ans = 0; read(n); read(m); dataset.push_back(-2); dataset.push_back(-1); for (int i = 0; i < n; ++i) { int x; read(x); dataset.push_back(x); } dataset.push_back(INF); sort(dataset.begin(), dataset.end()); root = buildTree(1, cnt = n+2, EMPTY); front = 1, back = cnt; while (m--) { int opt, x; read(opt), read(x); x ^= last; switch (opt) { case 1: //插入整数data->x splay(back); insertKth(createNode(x), getKth(findDataLeq(back, x))+1); break; case 2: //删除整数data->x splay(front); deleteKth(getKth(findDataGeq(front, x))); break; case 3: //查询x的排名 splay(back); last = getKth(findDataLeq(back, x-1)); ans ^= last; break; case 4: //查询第x个data last = t[findKth(root, x+1)].data; ans ^= last; break; case 5: //查询小于x的最大data splay(front); last = t[findDataLeq(front, x-1)].data; ans ^= last; break; case 6: //查询大于x的最小data splay(back); last = t[findDataGeq(back, x+1)].data; ans ^= last; break; } } write(ans); putchar('\n'); return 0; }

感谢支持!


__EOF__

本文作者炯炯目光
本文链接https://www.cnblogs.com/jjmg/p/13598480.html
关于博主:KTH 信息与网络工程硕士在读
版权声明:欢迎分享或转载
声援博主:To be or not to be, is a question.
posted @   Chiron-zy  阅读(245)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示