splay小记

学习了一下splay。

splay主要就是干线段树干的活,外加区间翻转,插入删除,还有分裂合并。

建树
就是普通的建成二叉树,为了方便以后的区间操作,会在两边建两个辅助的点。

int build(int l,int r,int y) // [l,r],父亲是y
{
    if(l > r) return 0;
    int mid = (l + r) / 2;
    int x = insert(a[mid]); f[x] = y; //建当前节点,insert是插入节点函数
    if(l == r) return x;
    tt[x][0] = build(l,mid - 1,x);
    tt[x][1] = build(mid + 1,r,x);
    pushup(x);//和线段树一样吧
    return x;
}

insert 函数

int insert(int key){
    int o;
    if(shan[0] > 0) o = shan[shan[0]--];
    else o = tot++;
    //因为splay有删除操作,所以要建一个栈(shan)来收容以前删除的点,重复利用
    ...
    //这里视题目而定,对点进行初始化
    return 0;
} 

splay 和 treap的不同主要是splay一般是把要操作的区间旋上去进行操作。而treap则给每个点加修正值,维护成堆的结构,来保证复杂度。所以splay最核心的就是splay操作了呗。

介绍splay操作就得先介绍rotate,旋转操作,和普通的平衡二叉树的旋转没有区别。

int son(int x){
    if(tt[f[x]][0] == x) return 0;else return 1;
    //这个函数用来判断x是父亲的哪个儿子
}

void rotate(int x){
    int y = f[x],z = son(x);
    //三步走
    tt[y][z] = tt[x][1 - z];
    if(tt[x][1 - z]) f[tt[x][1 - z]] = y;
    //先是把x的儿子接到y上
    f[x] = f[y];
    if(f[y]) tt[f[y]][son(y)] = x;
    //把x接到y的父亲上
    f[y] = x;
    tt[x][1 - z] = y;
    //y接到x上
    pushup(y),pushup(x);//转完要维护信息
    return;
}

void splay(int x,int y){//把x旋到y的子树上面去
    if(x == y) return;
    update(x,y);//由于x要旋转上去,所以要先把标记理一理
    while(f[x] != y){
        if(f[f[x]] != y){
            if(son(f[x]) == son(x)) rotate(f[x]);
            else rotate(x);//和普通avl树的旋转一样
        }
        rotate(x);
    }
    if(y == 0) root = x;
}

pushup和pushdown函数和线段树的并无二致。

del函数

void del(int x){
    if(x == 0) return;//空树不删了
    shan[++shan[0]] = x;
    del(tt[x][0]);del(tt[x][1]);
}

update函数

void update(int x,int y){
    do{
        d[++d[0]] = x;
        x = f[x];
    }while(x != y);
    while(d[0]) pushdown(x);
}//因为要从上往下pushdown,所以要用栈从下往上存起来

对区间[l,r]进行操作,先把l - 1旋到根节点,再把r + 1旋到l - 1的右儿子,那么[l,r]在r + 1的左儿子,该打标记打标记,该删删,该加加。

int x = kth(root,l - 1);splay(x,0);
int y = kth(root,r + 1);splay(y,x);
//balabala操作

其中kth操作就是二叉树找第k个节点。

分裂和合并
有的时候让把哪一段取出来放到前面还是后面,就要分裂和合并。

void split(int root,int k,int &l,int &r){
    int x = kth(root,k);splay(x,0);
    l = x;
    r = tt[x][1];
    f[r] = 0;
    tt[x][1] = 0;update(l);
    return;
}

void merge(int root1,int root2,int & newroot){
    int x = kth(root1,size[root1]);
    splay(x,0);
    t[x][1] = root2;
    f[root2] = x;
    update(root1);
    newroot = root1;
}

例题就是bzoj1500

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值