【模板】Splay(伸展树)普通平衡树(数据加强版)/洛谷P6136
题目链接
https://www.luogu.com.cn/problem/P6136
题目大意
需要写一种数据结构,来维护一些非负整数( \(int\) 范围内)的升序序列,其中需要提供以下操作:
- 插入一个整数 \(x\) 。
- 删除一个整数 \(x\) (若有多个相同的数,只删除一个)。
- 查询整数 \(x\) 的排名(排名定义为比当前数小的数的个数 \(+1\) )。
- 查询排名为 \(x\) 的数(如果不存在,则认为是排名小于 \(x\) 的最大数。保证 \(x\) 不会超过当前数据结构中数的总数)。
- 求 \(x\) 的前驱(前驱定义为小于 \(x\) ,且最大的数)。
- 求 \(x\) 的后继(后继定义为大于 \(x\) ,且最小的数)。
其中,初始序列大小为 \(n\) ,询问/操作总数为 \(m\) ,并要求 强制在线 。
数据范围 \(n \leq 10^5, m \leq 10^6\) , 单点时限 \(3s\) 。
题目解析
要求总复杂度为 \(O(N \log N)\) 级别,可以考虑伸展树( \(Splay\) ),对单个节点的查询、修改都维持在 \(O(\log N)\) 级别。
值得注意的是,这里代码中在数据集内多加入了一大( \(INF\) )一小( \(-1\) )两个虚设端点作为平衡树的上下限( \(front, back\) ),这样在实现查找、插入时可以避免越界,并能减少对于某些情况的特殊判断。
参考代码
#include
#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 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;
}
感谢支持!