Splay(还没完先咕着)
变量定义:
(n:节点个数~~~ch[x][2]~0代表x左儿子~1代表右儿子)
(val[x]x存储的值~~cnt[x]代表x存储的重复权个数~~fa[x]x爸爸~~siz[x]x子树下权值数)
操作:
chk,查询节点父亲方向
pushup,更新size数组值
void pushup(int x){siz[x] = siz[ch[x][0]]+siz[ch[x][1]]+cnt[x]}
旋转(rotate)
最开始树是这样的
我们把2号点整到4号点位置,2的下面就有子树1,3,4,5
一种优秀做法是把4->2改成4->3,6->4改成6->2,2->3改成2->4
splay需要rotate保持平衡,核心操作,旋转后中序遍历和合法性保持不变
void rotate(int x){
int y = fa[x],z = fa[y],k = chk(x),w = ch[x][k^1];
ch[y][k] = w,fa[w] = y;
ch[z][chk(y)] = x;fa[x] = z;
ch[x][k^1] = y;fa[y] = x;
pushup(y);pushup(x);
}
伸展(splay)
将一个节点一路rotate到指定节点的儿子。如果该点,fa,grandfa节点三点一线,应该先转fa
void splay(int x,int goal = 0){
while(fa[x] != goal){
int y =fa[x],z = fa[y];
if(z != goal){
if(chk(x) == chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
if(!goal) root = x;
}
find操作
void find(int x){
if(!root) return;
int cur = root;
while(ch[cur][x > val[cur]] && x != val[cur]){
cur = ch[cur][x > val[cur]];
}
splay(cur);
}
insert插入
从根节点开始,一路搜索下去。如果节点存在则直接自增cnt的值。否则新建节点并与父节点连边。因为新建节点时可能会拉出一条链,所以新建节点后需要将该节点splay到根节点。沿途的rotate操作可以使平衡树恢复平衡。
void insert(int x){
int cur = root,p = 0;
while(cur && val[vur] != x){
}
}