splay模板随手记

· · 个人记录

实现splay操作:

序言:

OiWiki传送门

一:初始操作

1.初始化:

int rt,id;
//树根和总节点编号 
int f[N],ch[N][2],val[N];
//父亲,左右儿子,值 
int cnt[N],sz[N];
//副本数,子树大小 

2.判左右儿子:

右儿子为1.左儿子为0

inline bool dir(int x){
    return x==ch[f[x]][1];
}

3.单点更新子树大小:

注意:一个节点的子树大小表示其左子树大小,右子树大小和此节点副本数之和

inline void push_up(int x){
    sz[x]=cnt[x]+sz[ch[x][0]]+sz[ch[x][1]];
}

4.单旋操作(zig,zag融合):

inline void rotate(int x){
    int y=f[x],z=f[y];//y为x父亲,z为x祖父 
    bool r=dir(x); 
    ch[y][r]=ch[x][!r];//y代表x的子树连接x的另一子树 
    ch[x][!r]=y;//x的另一子树更新为y 
    if (z) ch[z][dir(y)]=x;
    //如果x有祖父节点,让x取代原y位置 
    if (ch[y][r]) f[ch[y][r]]=y;
    //如果y原来代表x的子树位置现有子树,让子树认y做父亲
    //第四行已经更新儿子关系,未更新父亲关系 
    f[y]=x;f[x]=z;
    //让y反认父亲,让x认祖父当父亲(取代y节点位置) 
    push_up(y);//更新y子树关系 
    push_up(x);//更新x子树关系 
}

5.splay伸展操作(将x旋转到根):

inline void splay(int& z,int x){
    //z为根节点,x为上旋节点 
    int w=f[z];//设w为根节点z的父亲(当x的父亲为w时,x就是根了) 
    for (int y;(y=f[x])!=w;rotate(x))//y是x父亲 
        if (f[y]!=w)//当旋转操作还差>=2步到根时 
            rotate(dir(x)==dir(y)?y:x);
            //当x和y同为左/右子树启用zig-zig(转y 转x)
            //当x和y左右子树性质不同用zig-zag(转x 转x)
            //当旋转操作只差一步到根时只需旋转x到根 
    z=x;//把z(即rt)更新为x 
}