splay学习

一个神奇的数据结构,可以搞来搞去,还很快?
他的优化就是每次询问一个点的时候,就暴力吧这个点更新为根(用tarjan)给出的更新方式,就可以做到n次操作,平摊下来每次的复杂度是logn,证明的话请见 大牛博客

我还是抱着一定的怀疑的,每次暴力更新,真的不会出问题,然后就去找了一个模板题,结果,真的没有出问题,emmmm,太强了,,,
hdu 1754

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
struct splaytree{
    int ch[N][2],fa[N],id[N],pos[N],dat[N],mx[N];
    //ch 左右儿子 fa 父亲 id 节点下标 pos i下标所在节点 dat数据 mx子树最大值 root 根
    int top,root;
    void init(){
        top = 1;
        root =0;
    }
    int newnode(int f){
        fa[top] = f;
        ch[top][0] = ch[top][1] = 0;
        mx[top] = 0;
        return top++;
    }
    void update(int x){
        mx[x] = max(mx[ch[x][0]],mx[ch[x][1]]);
        mx[x] = max(mx[x],dat[x]);
    }
    void insert(int x,int d){
        int tmp = root;
        int y = 0;
        while(true){
            if(tmp == 0){
                tmp = newnode(y);
                id[tmp] = x;
                pos[x] = tmp;
                dat[tmp] = d;
                if(x < id[y]) ch[y][0] = tmp;
                else ch[y][1] = tmp;
                splay(tmp,0);
                root = tmp;
                return;
            }
            y = tmp;
            int dir = x > id[tmp];
            tmp = ch[tmp][dir];
        }
    }
    int getson(int x){
        return x==ch[fa[x]][1];
    }
    void rotate(int x){
        int dir = !getson(x);
        int p = fa[x];
        int pf = fa[fa[x]];
        ch[pf][fa[x] == ch[pf][1]] = x;
        if(ch[x][dir]) fa[ch[x][dir]] = p;
        fa[x] = fa[p];
        fa[p] = x;
        ch[p][!dir] = ch[x][dir];
        ch[x][dir] = p;
        update(p);
        update(x);
        //print();
    }
    void splay(int x,int y){
        while(true){
            if(fa[x] == y) return;
            if(fa[fa[x]] != y){
                if(getson(x) == getson(fa[x])) rotate(fa[x]);
                else rotate(x);
            }
            rotate(x);
        }
        root = x;
    }
    int get(int l,int r){
        splay(pos[l-1],0);
        splay(pos[r+1],pos[l-1]);
        return mx[ch[pos[r+1]][0]];
    }
    void set(int x,int y){
        splay(pos[x],0);
        dat[pos[x]] = y;
        mx[pos[x]] = y;
    }
    void print(){
        for(int i = 1;i < top;i ++){
            cout << i << ' '<< ch[i][0] << ' '<< ch[i][1] << ' '<< dat[i] << ' '<< mx[i] <<' '<< fa[i] << ' '<<id[i]<< endl;
        }
        for(int i = 1;i <= 5;i ++){
            cout << i << ' '<< pos[i] << endl;
        }
    }
}spt;


int main(){
    int n,m;
    while(scanf("%d %d",&n,&m)==2){
        spt.init();
        spt.insert(0,0);
        for(int i = 1;i <= n;i ++){
            int now;
            scanf("%d",&now);
            spt.insert(i,now);
        }
        spt.insert(n+1,0);
        for(int i = 1;i <= m; i++){
            char st[10];
            int a,b;
            scanf("%s %d %d",st,&a,&b);
            if(st[0] == 'Q'){
                printf("%d\n",spt.get(a,b));
            }
            else spt.set(a,b);
        }
    }
    return 0;
}

下面讲一下splay的一些应用:
1.查询区间,先把l-1变成跟,再把r+1扭到l-1下面,那么r+1的右儿子就是所有的点了,
2.删除点,先把这个点扭到根,看看儿子的情况,
没有儿子,直接删除,
只有一个儿子,把这个儿子变成根,
有两个儿子,先把这给点扭到根,然后把比这个数小的最大的数扭到他的下面,然后把这个点的右儿子放到这个点的左儿子的右儿子就好了。

猜你喜欢

转载自blog.csdn.net/zstu_zy/article/details/81669830