洛谷P3369 普通平衡树 (平衡树模板!!!)

版权声明:本文为博主原创文章,喜欢就点个赞吧 https://blog.csdn.net/Anxdada/article/details/81780427

传送门
这道题用各种平衡树都能过. 什么Splay、Treap、SBT、替罪羊树, 红黑树等等. 因为最近学LCT用的是splay, 所以就用的平衡树来写的.

这里面没有翻转操作, 我知道是打个标记即可, 可是真正的做翻转序列的题, 还是不太会啊…

AC Code

const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int root = 0, tot;
struct Node {
    int fa, son[2], cnt, val, scnt;
}t[maxn];
void update(int u) {
    t[u].scnt = t[t[u].son[0]].scnt + t[t[u].son[1]].scnt + t[u].cnt;
}
bool nroot(int x) {
    return t[t[x].fa].son[0] == x || t[t[x].fa].son[1] == x;
}
void rot(int x) {
    int fa = t[x].fa, gfa = t[t[x].fa].fa;
    int k = (x == t[fa].son[1]);
    t[fa].son[k] = t[x].son[k^1];
    if(nroot(fa)) t[gfa].son[fa == t[gfa].son[1]] = x;
    if(t[x].son[k^1]) t[t[x].son[k^1]].fa = fa;
    t[x].son[k^1] = fa;
    t[fa].fa = x; t[x].fa = gfa;
    update(fa); update(x);
}
void splay(int x, int pos) {
    for(int fa ; t[x].fa != pos ; rot(x)) {
        if (fa = t[x].fa, t[fa].fa != pos)
            rot((t[x].son[0] == x) ^ (t[fa].son[0] == fa) ? fa : x);
    }
    if(!pos) root = x;
}

void Insert(int x) {
    int u = root, pre = 0;
    while(u && t[u].val != x) {
        pre = u;
        u = t[u].son[x > t[u].val];
    }
    if(u) t[u].cnt++;
    else {
        u = ++tot;
        if(pre) t[pre].son[x > t[pre].val] = u;
        t[tot].son[0] = t[tot].son[1] = 0;
        t[tot].fa = pre; t[tot].val = x;
        t[tot].cnt = 1; t[tot].scnt = 1;
    }
    splay(u, 0);
}

void Find(int x) {
    int u = root;
    if(!u) return;
    while(t[u].son[x > t[u].val] && x != t[u].val)
        u = t[u].son[x > t[u].val];
    splay(u, 0);
}

int Next(int x, int f) {
    Find(x);
    int u = root;
    if((t[u].val > x && f) || (t[u].val < x && !f)) return u;
    u = t[u].son[f];
    while(t[u].son[f^1]) u = t[u].son[f^1];
    return u;
}

void Delete(int x) {
    int last = Next(x, 0);
    int next = Next(x, 1);
    splay(last, 0); splay(next, last);
    int del = t[next].son[0];
    if(t[del].cnt > 1) {
        t[del].cnt--;
        splay(del, 0);
    }
    else t[next].son[0] = 0;
}
int K_th(int x) {
    int u = root;
    if(t[u].scnt < x)
        return -1;
    while(1) {
        int y = t[u].son[0];
        if(x > t[y].scnt + t[u].cnt) {
            x -= t[y].scnt + t[u].cnt;
            u = t[u].son[1];
        }
        else if(t[y].scnt >= x) u = y;
        else return t[u].val;
    }
}
void solve() {
    Insert(-inf); Insert(inf);
    int n; scanf("%d", &n);
    while(n--) {
        int op, x; scanf("%d%d", &op, &x);
        if(op == 1) Insert(x);
        else if(op == 2) Delete(x);
        else if (op == 3) {
            Find(x); printf("%d\n", t[t[root].son[0]].scnt);
        }
        else if (op == 4) printf("%d\n", K_th(x+1));
        else if (op == 5) printf("%d\n", t[Next(x, 0)].val);
        else printf("%d\n", t[Next(x, 1)].val);
    }
}

避免忘记, 附上解释版

const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int root = 0, tot;
struct Node {
    int fa, son[2], cnt, val, scnt;
    // 父亲, 两个儿子, 自身的数量, 值, 儿子和自身一共的数量
}t[maxn];
void update(int u) {  // 计算儿子数量
    t[u].scnt = t[t[u].son[0]].scnt + t[t[u].son[1]].scnt + t[u].cnt;
}
bool nroot(int x) {
    return t[t[x].fa].son[0] == x || t[t[x].fa].son[1] == x;
}
void rot(int x) {
    int fa = t[x].fa, gfa = t[t[x].fa].fa;
    int k = (x == t[fa].son[1]);
    t[fa].son[k] = t[x].son[k^1];
    if(nroot(fa)) t[gfa].son[fa == t[gfa].son[1]] = x;
    if(t[x].son[k^1]) t[t[x].son[k^1]].fa = fa;
    t[x].son[k^1] = fa;
    t[fa].fa = x; t[x].fa = gfa;
    update(fa); update(x);
}
void splay(int x, int pos) { //把x节点旋转到目标位置
    for(int fa ; t[x].fa != pos ; rot(x)) {
        if (fa = t[x].fa, t[fa].fa != pos)
            rot((t[x].son[0] == x) ^ (t[fa].son[0] == fa) ? fa : x);
    }
    if(!pos) root = x;  //当前的根节点
}

void Insert(int x) { //插入x
    int u = root, pre = 0;
    while(u && t[u].val != x) {
        pre = u;
        u = t[u].son[x > t[u].val];
    }
    if(u)//已经有这个数字了
        t[u].cnt++;//计算数字个数
    else {//不存在这个数字,加入新的节点
        u = ++tot;//总的节点数
        if(pre) t[pre].son[x > t[pre].val] = u;
        t[tot].son[0] = t[tot].son[1] = 0;
        t[tot].fa = pre; t[tot].val = x;
        t[tot].cnt = 1; t[tot].scnt = 1;
    }
    splay(u, 0);  // 继续保持平衡
}

void Find(int x) { //查找x的位置(或者是最接近x的位置)
    int u = root;
    if(!u) return;//不存在节点,无法查找排名
    while(t[u].son[x > t[u].val] && x != t[u].val) //找到x所在的位置
        u = t[u].son[x > t[u].val];
    splay(u, 0);
}

int Next(int x, int f) { //查找前驱/后继
    Find(x); //查找x的位置(Splay操作到根节点)
    int u = root;
    if((t[u].val > x && f) || (t[u].val < x && !f)) return u; //返回结果
    u = t[u].son[f];
    while(t[u].son[f^1]) u = t[u].son[f^1];
    return u;
}

void Delete(int x) {  //删除x
    int last = Next(x, 0); //查找前驱
    int next = Next(x, 1); //查找后继
    splay(last, 0); splay(next, last);
    int del = t[next].son[0];
    if(t[del].cnt > 1) {
        t[del].cnt--; //存在多个这个数字,直接减去一个
        splay(del, 0);
    }
    else t[next].son[0] = 0; //清除掉节点
}
int K_th(int x) { //查找排名为x的值
    int u = root;
    if(t[u].scnt < x) //不存在这么多个数
        return -1;
    while(1) {
        int y = t[u].son[0];
        if(x > t[y].scnt + t[u].cnt) { //在排名在u的后面
            x -= t[y].scnt + t[u].cnt; //直接减掉这么多
            u = t[u].son[1]; //在右子树中继续找
        }
        else if(t[y].scnt >= x) //如果y的节点数多于x
            u = y;       //在左子树中继续查找
        else return t[u].val;//否则找到了结果,直接返回
    }
}
void solve() {
    Insert(-inf); Insert(inf);
    // 这样的话就可以保证前驱和后继一定能找得到, 不过后面注意计数的变化.
    int n; scanf("%d", &n);
    while(n--) {
        int op, x; scanf("%d%d", &op, &x);
        if(op == 1) Insert(x);
        else if(op == 2) Delete(x);
        else if (op == 3) {
            Find(x); printf("%d\n", t[t[root].son[0]].scnt);
        }
        else if (op == 4) printf("%d\n", K_th(x+1));
        else if (op == 5) printf("%d\n", t[Next(x, 0)].val);
        else printf("%d\n", t[Next(x, 1)].val);
    }
}

猜你喜欢

转载自blog.csdn.net/Anxdada/article/details/81780427
今日推荐