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

题目链接

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

题目大意

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

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

感谢支持!

posted @ 2020-09-01 20:41  Chiron-zy  阅读(234)  评论(0编辑  收藏  举报