[算法学习] 平衡树 splay
基本信息维护
struct Splay {
int sz, fa, val, cnt, ch[2];
//sz 表示子树大小
//ch[0 / 1]表示左右两个儿子
//fa 表示父亲
//cnt 表示该点的值出现了几次
//val 节点的值
} t[N];
#define ls(p) (t[p].ch[0])
#define rs(p) (t[p].ch[1])
新建节点(采用回收空间的写法)
int top, rb[N];
inline int newnode(ll _val) {
int x = top ? rb[top -- ] : ++ sz;
t[x].ch[0] = t[x].ch[1] = t[x].fa = 0; t[x].sz = 1;
t[x].val = t[x].sum = _val; t[x].cnt = _val < 0 ? 1 : 0;
return x;
}
Rotate
1.X变到原来Y的位置
2.Y变成了 X原来在Y的 相对的那个儿子
3.Y的非X的儿子不变 X的 X原来在Y的 那个儿子不变
4.X的 X原来在Y的 相对的 那个儿子 变成了 Y原来是X的那个儿子
inline void rotate(int p) {
int f = t[p].fa, d = t[f].ch[0] == p ? 1 : 0;
t[p].fa = t[f].fa; t[f].fa = p; t[t[p].ch[d]].fa = f;
t[f].ch[d ^ 1] = t[p].ch[d]; t[p].ch[d] = f;
if(t[p].fa) t[t[p].fa].ch[t[t[p].fa].ch[0] == f ? 0 : 1] = p;
pushup(f); pushup(p);
}
Splay
inline void splay(int p, int goal = 0) {
while(t[p].fa != goal) {
int f = t[p].fa, g = t[f].fa;
if(g != goal) rotate((t[f].ch[0] == p) == (t[g].ch[0] == f) ? f : p);
rotate(p);
}
if(!goal) rt = p;
}
建树
inline int build(int l, int r, int *a) {
if(l > r) return 0;
if(l == r) return newnode(a[l]);
int mid = (l + r) >> 1, p = newnode(a[mid]);
ls(p) = build(l, mid - 1, a);
rs(p) = build(mid + 1, r, a);
t[ls(p)].fa = t[rs(p)].fa = p;
pushup(p);
return p;
}
int main() {
rt = build(1, n, a);
}
在第(x)位置后插入长度为(tot)的区间
inline void insert(int x, int tot, int *a) {
int p = build(1, tot, a);
int u = find_kth(rt, x); splay(u);
int v = find_kth(rt, x + 1); splay(v, u);
t[v].ch[0] = p; t[p].fa = v;
pushup(v); pushup(u);
}
删除区间[l,r]
inline void recycle(int p) {
if (ls(p)) recycle(ls(p));
if (rs(p)) recycle(rs(p));
rb[++top] = p;
}
inline void erase(int l, int r) {
int u = find_kth(rt, l - 1); splay(u);
int v = find_kth(rt, r + 1); splay(v, u);
recycle(t[v].ch[0]); t[v].ch[0] = 0;
pushup(v); pushup(u);
}
区间求和
inline int query_sum(int l, int r) {
int u = find_kth(rt, l - 1); splay(u);
int v = find_kth(rt, r + 1); splay(v, u);
printf("%lld
", t[ls(v)].sum);
}
找到区间排名为(k)的数
inline int find_kth(int p, int k) {
// pushdown(p); (有lazy tag时这么写)
if(t[ls(p)].sz + 1 == k) return p;
else if(t[ls(p)].sz >= k) return find_kth(ls(p), k);
else return find_kth(rs(p), k - t[ls(p)].sz - 1);
}
循环版找到区间排名为(k)的数
inline int find_kth(int k) {
int now = rt;
while(now) {
if(k <= t[ls(now)].sz) now = ls(now);
else {
k -= t[ls(now)].sz + t[now].cnt;
if(k <= 0) return t[now].val;
else now = rs(now);
}
}
}
循环版找前驱和后缀
inline int getpre(int v, bool getid = false) {
int ans = -INF, id = 0, now = rt;
while(now) {
if(t[now].val >= v) now = ls(now);
else {
if(t[now].val > ans) ans = t[now].val, id = now;
now = rs(now);
}
}
return getid ? id : ans;
}
inline int getsuf(int v, bool getid = false) {
int ans = INF, id = 0, now = rt;
while(now) {
if(t[now].val <= v) now = rs(now);
else {
if(t[now].val < ans) ans = t[now].val, id = now;
now = ls(now);
}
}
return getid ? id : ans;
}
[算法学习] 平衡树 splay
原文地址:https://www.cnblogs.com/Hock/p/13437113.html