浅谈伸展树(Splay)

Posted chandery

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈伸展树(Splay)相关的知识,希望对你有一定的参考价值。

//本文是一个暂时的小记,有不对的请大佬们指出~
真正大佬的在这http://blog.csdn.net/clove_unique/article/details/50630280

伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由丹尼尔·斯立特Daniel Sleator和罗伯特·恩卓·塔扬Robert Endre Tarjan在1985年发明的。
在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。
它的优势在于不需要记录用于平衡树的冗余信息。

(本段来自百度)

Splay的基本操作

  • get操作
    主要用来查找x结点属于其父亲结点的左儿子还是右儿子
int get(int x)

    return son[fa[x]][1]==x;
  • update操作
    主要是更新size值
    (有的时候也会更新其他)
void update(int x)
  
     if (x)
       
          size[x]=1; 
          if (son[x][0]) size[x]+=size[son[x][0]];  
          if (son[x][1]) size[x]+=size[son[x][1]]; 
       
  
  • rotate(旋转)操作
    这是Splay最常用也是最重要的操作了
    具体的操作就是找到x与父亲的关系(是他的左儿子还是右儿子),然后旋上去,直接取代其父亲的位置,然后进行一系列的“儿子认领“(具体看代码)
int rotate(int x)

    int k=get(x),y=fa[x];
    if(fa[y])son[fa[y]][get(y)]=x;
    if(son[x][!k])fa[son[x][!k]]=y;//x结点反方向的儿子
    fa[x]=fa[y],fa[y]=x,son[y][k]=son[x][!k],son[x][!k]=y;//认领儿子
    update(y),update(x);

然后来一道例题,相信用线段树做过【最大值】,此题甚好,但是又有了区间修改,变成了【最大值2】,现在用Splay实现:
题目不记得了吧~

Description

 在N(1<=N<=100000)个数A1…An组成的序列上进行M(1<=M<=100000)次操作,操作有两种:
  (1)1 L R C:表示把A[L]到A[R]增加C(C的绝对值不超过10000);
  (2)2 L R:询问A[L]到A[R]之间的最大值。

思考一下~

好的,那么我们只需要在上面讲的所有里面加入一个打标记的操作就好了。
这和线段树的差不多,打完这个标记lazy,下传~

#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
int son[100001][2];//Son
LL fmax[100001];//Ans
int fa[100001];//Father
LL add[100001];//Lazy
LL a[100001];//Data
int d[100001];//Queue
int n,m,kind,l,r,c;
int update(int x)fmax[x]=max(a[x],max(fmax[son[x][0]],fmax[son[x][1]]));
int lazy(int x,int c)fmax[x]+=c,a[x]+=c,add[x]+=c;
int get(int x)return son[fa[x]][1]==x;
int clear(int x)

    if(son[x][0])lazy(son[x][0],add[x]);
    if(son[x][1])lazy(son[x][1],add[x]);
    add[x]=0;

int remove(int x,int y)

    d[0]=0;
    for (x=x;x-y;x=fa[x]) d[++d[0]]=x;
    while (d[0]) clear(d[d[0]--]);  

int rotate(int x)

    int k=get(x),y=fa[x];
    if(fa[y])son[fa[y]][get(y)]=x;
    if(son[x][!k])fa[son[x][!k]]=y;
    fa[x]=fa[y],fa[y]=x,son[y][k]=son[x][!k],son[x][!k]=y;
    update(y),update(x);

int Splay(int x,int y)

    remove(x,y);
    while(fa[x]-y)
    
        if(fa[fa[x]]-y)
            if(get(x)==get(fa[x])) rotate(fa[x]);
            else rotate(x);
        rotate(x);
    

int main()

    freopen("Max2.in","r",stdin);
    scanf("%d",&n);
    int i;fmax[0]=-1e9;
    for (i=1;i<=n;++i)
        scanf("%lld",&a[i+1]),fa[i]=i+1,son[i+1][0]=i,update(i+1);
    fa[n+1]=n+2,son[n+2][0]=n+1,update(n+2);
    scanf("%d",&m);
    for (i=1;i<=m;++i)
    
        scanf("%d%d%d",&kind,&l,&r);++l,++r;
        Splay(l-1,0),Splay(r+1,l-1);
        if(kind-2)scanf("%d",&c),lazy(son[r+1][0],c);
        else printf("%lld\n",fmax[son[r+1][0]]);
    

JZOJ【NOIP2015模拟9.12】平方和

#include<cstdio>
#include<iostream>
#define maxn 200010
#define mo 7459
#define ll long long
using namespace std;
struct Moon
    ll tag,key,size;
    ll sum,sum2,fa;
t[4*maxn];
ll son[maxn][2],d[maxn];
ll n,a[maxn],m,x,y,z,root,sz;
ll get(ll x)

    return son[t[x].fa][1]==x;

void update(ll x)

    t[x].size=t[son[x][0]].size+t[son[x][1]].size+1;
    (t[x].sum=t[son[x][0]].sum+t[son[x][1]].sum+t[x].key)%=mo;
    (t[x].sum2=t[son[x][0]].sum2+t[son[x][1]].sum2+t[x].key*t[x].key)%=mo;

ll findx(ll num)

    ll v=root;
    while(x)
    
        if(son[v][0]&&t[son[v][0]].size>=num) v=son[v][0];
        else 
        
            ll cnt=t[son[v][0]].size+1;
            if(cnt==num) return v;
            num-=cnt;
            v=son[v][1];
        
    
    return v;

void rotate(ll x)

    ll k=get(x),y=t[x].fa;
    if(t[y].fa) son[t[y].fa][get(y)]=x;
    if(son[x][!k]) t[son[x][!k]].fa=y;
    t[x].fa=t[y].fa,son[y][k]=son[x][!k];
    son[x][!k]=y,t[y].fa=x;
    update(y),update(x);

ll Sum(ll x,ll b)

    ll ans=((t[x].sum2+2*b*t[x].sum)%mo+t[x].size*b*b%mo)%mo;
    return ans;

void clear(ll x)

    if(t[x].tag)
    
        for (ll i=0;i<=1;++i)
        
            if(!son[x][i]) continue;
            (t[son[x][i]].key+=t[x].tag)%=mo;
            (t[son[x][i]].tag+=t[x].tag)%=mo;
            t[son[x][i]].sum2=Sum(son[x][i],t[x].tag%mo);
            (t[son[x][i]].sum+=t[son[x][i]].size*t[x].tag)%=mo;
        
        t[x].tag=0;
    

void remove(ll x,ll y)

    d[0]=0;
    for (;x!=y;x=t[x].fa) d[++d[0]]=x;
    for (ll i=d[0];i>=1;--i) clear(d[i]);
 
void splay(ll x,ll y)

    remove(x,y);
    while(t[x].fa!=y)
    
        if(t[t[x].fa].fa!=y) 
            if(get(x)==get(t[x].fa)) rotate(t[x].fa);
            else rotate(x);
        rotate(x);
    
    if(y==0) root=x;

void add(ll x,ll c)

    (t[x].key+=c)%=mo;
    (t[x].tag+=c)%=mo;
    t[x].sum2=Sum(x,c);
    (t[x].sum+=t[x].size*c%mo)%=mo;

int main()

    scanf("%lld",&n);
    ll i,j,xx,yy;char k;
    for (i=1;i<=n;++i) scanf("%lld",&a[i+1]);
    for (i=2;i<=n+2;++i) son[i][0]=i-1,t[i-1].fa=i,t[i-1].key=a[i-1],update(i-1);
    update(n+2);
    scanf("%lld\n",&m);root=sz=n+2;
    for (i=1;i<=m;++i)
    
        scanf("%c",&k);
        if(k=='Q')
        
            scanf("uery%lld%lld\n",&x,&y);++x,++y;
            yy=findx(y+1),xx=findx(x-1);
            splay(xx,0);
            splay(yy,xx);
            printf("%lld\n",(t[son[yy][0]].sum2+mo)%mo);
        
        if(k=='I')
        
            scanf("nsert%lld%lld\n",&x,&y);++x; 
            xx=findx(x-1),yy=findx(x);
            splay(xx,0),splay(yy,xx);
            son[yy][0]=++sz,t[sz].fa=yy;
            t[sz].key=t[sz].sum=y,t[sz].sum2=y*y%mo,t[sz].size=1;
            update(yy),update(xx);
        
        if(k=='A')
        
            scanf("dd%lld%lld%lld\n",&x,&y,&z);++x,++y;
            yy=findx(y+1),xx=findx(x-1);
            splay(xx,0),splay(yy,xx);
            add(son[yy][0],z);
        
    

以上是关于浅谈伸展树(Splay)的主要内容,如果未能解决你的问题,请参考以下文章

Splay Tree(伸展树)

# 伸展树 Splay

伸展树(splay tree)

Splay伸展树学习笔记

伸展树(Splay)详解

算法学习:伸展树(splay)