[BZOJ4825][Hnoi2017]单旋(LCT+set)

手动模拟,可以发现:单旋最小值或最大值到根,实际上就是删掉了 2 条边又加上了2条边。
而对于维护深度,由于根节点不断变化,所以树中所有节点的深度也在不断地整体变化。因此考虑用一个LCT维护这棵树,这样查询一个节点的深度,就是查询一个点到根的距离。同时又能支持动态加边和删边。
所以,现在就剩下一个问题:如何确定新节点的位置。
以下,设 x 为待插入的节点,vx为节点 x 的关键码(这里x泛指任何节点,不仅仅指待插入的节点)。
假设节点 x 应该作为节点y的右子节点。则在插入节点 x 之前,节点y一定没有右子节点,此时 y 在树中的后继一定是:
y节点的父节点开始走,如果 y 在当前节点的左子树内,那么当前节点就是y的后继。否则继续往当前节点的父节点走,直到满足条件为止。
简单地说,节点 y 在其后继节点(下面记为z)的左子树内。
这时候,如果节点 x 作为了节点y的右子节点,那么显然节点 x 也在节点z的左子树内。所以这时候一定存在 vy<vx<vz
对于节点 x 作为其他节点的左子节点,也是同理。所以,得出结论:
新插入的节点,一定是这个节点在树中的前驱的右子节点,或者后继的左子节点
同时,也可以得到,对于任意一个关键码k,如果关键码 k 在树中有前驱和后继,那么「关键码k的前驱有右子树」和「关键码 k <script type="math/tex" id="MathJax-Element-48">k</script>的后继有左子树」这两个事件有且只有一个会发生。
所以,用一个set维护数集,就能较方便地找到插入位置了。
代码:

#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 4e5 + 5;
int n, T, fa[N], lc[N], rc[N], sze[N], rev[N], len, que[N], val[N],
L[N], R[N], F[N], Root, ans;
struct cyx {
    int id;
    cyx() {}
    cyx(int _id) : id(_id) {}
    friend inline bool operator == (const cyx &a, const cyx &b) {
        return a.id == b.id;
    }
    friend inline bool operator < (const cyx &a, const cyx &b) {
        return val[a.id] < val[b.id];
    }
    friend inline bool operator > (const cyx &a, const cyx &b) {
        return val[a.id] > val[b.id];
    }
    friend inline bool operator <= (const cyx &a, const cyx &b) {
        return val[a.id] <= val[b.id];
    }
    friend inline bool operator >= (const cyx &a, const cyx &b) {
        return val[a.id] >= val[b.id];
    }
};
set<cyx> orz;
set<cyx>::iterator pyz;
int which(int x) {return rc[fa[x]] == x;}
bool is_root(int x) {
    return !fa[x] || (lc[fa[x]] != x && rc[fa[x]] != x);
}
void down(int x) {
    if (rev[x]) {
        swap(lc[x], rc[x]);
        if (lc[x]) rev[lc[x]] ^= 1;
        if (rc[x]) rev[rc[x]] ^= 1;
        rev[x] = 0;
    }
}
void upt(int x) {
    sze[x] = 1;
    if (lc[x]) sze[x] += sze[lc[x]];
    if (rc[x]) sze[x] += sze[rc[x]];
}
void rotate(int x) {
    int y = fa[x], z = fa[y], b = lc[y] == x ? rc[x] : lc[x];
    if (z && !is_root(y)) (lc[z] == y ? lc[z] : rc[z]) = x;
    fa[x] = z; fa[y] = x; if (b) fa[b] = y;
    if (lc[y] == x) rc[x] = y, lc[y] = b;
    else lc[x] = y, rc[y] = b; upt(y); upt(x);
}
void splay(int x) {
    int i, y; que[len = 1] = x;
    for (y = x; !is_root(y); y = fa[y]) que[++len] = fa[y];
    for (i = len; i; i--) down(que[i]);
    while (!is_root(x)) {
        if (!is_root(fa[x])) {
            if (which(x) == which(fa[x])) rotate(fa[x]);
            else rotate(x);
        }
        rotate(x);
    }
}
void Access(int x) {
    int y; for (y = 0; x; y = x, x = fa[x]) {
        splay(x); rc[x] = y;
        if (y) fa[y] = x; upt(x);
    }
}
void MakeRoot(int x) {
    Access(x); splay(x); rev[x] ^= 1;
}
void Link(int x, int y) {
    MakeRoot(x); fa[x] = y;
}
void Cut(int x, int y) {
    MakeRoot(x); Access(y); splay(y);
    lc[y] = fa[x] = 0; upt(y);
}
int Query(int x, int y) {
    MakeRoot(x); Access(y); splay(y);
    return sze[y];
}
void splayMin() {
    pyz = orz.begin(); int x = pyz->id, y = R[x], z = F[x];
    ans = Query(Root, x); if (x == Root) return;
    if (y) Cut(x, y); Cut(x, z); if (y) Link(z, y);
    Link(x, Root); F[R[x] = Root] = x; F[Root = x] = 0;
    L[z] = y; if (y) F[y] = z;
}
void splayMax() {
    pyz = orz.end(); int x = (*--pyz).id, y = L[x], z = F[x];
    ans = Query(Root, x); if (x == Root) return;
    if (y) Cut(x, y); Cut(x, z); if (y) Link(z, y);
    Link(x, Root); F[L[x] = Root] = x; F[Root = x] = 0;
    R[z] = y; if (y) F[y] = z;
}
int pre(cyx x) {
    pyz = orz.find(x);
    return pyz == orz.begin() ? 0 : (*--pyz).id;
}
int suf(cyx x) {
    pyz = orz.find(x);
    return ++pyz == orz.end() ? 0 : pyz->id;
}
int main() {
    int i, op; n = read();
    for (i = 1; i <= n; i++) sze[i] = 1; while (n--) {
        op = read(); if (op == 1) {
            val[++T] = read(); orz.insert(cyx(T));
            if (!Root) {Root = T; printf("1\n"); continue;}
            int p = pre(cyx(T)), s = suf(cyx(T));
            if (p && !R[p]) F[R[p] = T] = p, Link(p, T);
            if (s && !L[s]) F[L[s] = T] = s, Link(s, T);
            ans = Query(Root, T);
        }
        else if (op == 2) splayMin();
        else if (op == 3) splayMax();
        else if (op == 4) {
            splayMin(); pyz = orz.begin(); orz.erase(pyz);
            if (!L[Root] && !R[Root]) {Root = 0; continue;}
            Cut(Root, R[Root]); Root = R[Root]; F[Root] = 0;
        }
        else {
            splayMax(); pyz = orz.end(); orz.erase(--pyz);
            if (!L[Root] && !R[Root]) {Root = 0; continue;}
            Cut(Root, L[Root]); Root = L[Root]; F[Root] = 0;
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值