Splay

伸展树的作用和非旋转Treap类似,只不过它依然通过旋转来维持平衡,双旋的情况稍微复杂一点(单旋无法保证均摊\(O(logn)\)),主要有三种操作:

  • splay(k):将第k大元素旋转至根节点
  • merge(x,y):合并两棵splay,返回新的根节点
  • split(x,k):将元素[1,k]和[k+1,n]分离,返回两棵新树的根节点

其他维护操作可以用标记完成,建树直接二分。

代码:

struct Node{
 Node *ch[2];
 int v,size,flip;
 
 Node(int val,Node *null) {v=val,size=1,flip=0,ch[0]=ch[1]=null;}
 int cmp(int k)
 {
  if ((ch[0]->size+1)==k) return -1;
  return ((ch[0]->size+1)<k)?(1):(0);
 }
 void maintain() {size=ch[0]->size+ch[1]->size+1;}
 void pushdown() {if (flip) swap(ch[0],ch[1]),ch[0]->flip^=1,ch[1]->flip^=1,flip=0;}
} *null,*root;


//-------------------------------------

Node *newnode(int v) {return new Node(v,null);}

Node* rotate(Node* &o,int d)
{
 Node* k=o->ch[d^1];o->ch[d^1]=k->ch[d],k->ch[d]=o;
 o->maintain(),k->maintain(),o=k;
}

void splay(Node* &o,int k)
{
  o->pushdown();int d=o->cmp(k);
 if (d==1) k-=o->ch[0]->size+1;

 if (d!=-1)
 {
  Node* p=o->ch[d];p->pushdown();
  int d2=p->cmp(k),k2=(d2==0)?(k):(k-p->ch[0]->size-1);
  if (d2!=-1)
  {
   splay(p->ch[d2],k2);
   if (d==d2) rotate(o,d^1); else rotate(p,d2^1);
  }
  rotate(o,d^1);
 }
}

Node* merge(Node *x,Node *y)
{
 if (x==null) return y;
 splay(x,x->size);
 x->ch[1]=y,x->maintain();
 return x;
}

Node* split(Node *o,int k,Node* &l,Node* &r)
{
 splay(o,k);
 if (o->flip)`
o->pushdown();
 l=o,r=o->ch[1],o->ch[1]=null,l->maintain();
}

Splay 2.0

我们在splay上加上维护父节点与tag,可以做到额外的一些操作:

  • rank(x):对于一个已知指针的结点,查出其在Splay中的是第几位。
  • sum/min/maxi(x,l,r):查找给定的Splay树x,区间\([l,r]\)的返回值。

代码:

struct Node{
 Node *ch[2],*f;
 int v,size;
 LL s,sum,tag; //当前结点值,维护值,标记,
 Node(int val,Node *null) {v=val,s=0,sum=0,size=1,tag=0,ch[0]=ch[1]=f=null;}

 void maintain()
 {
  size=ch[0]->size+ch[1]->size+1;
  sum=ch[0]->sum+ch[1]->sum+s; //维护当前维护值
 }
 void pushdown() //下放结点,类似于线段树的更新方式
 {
  if (tag) ch[0]->update(tag),ch[1]->update(tag),tag=0;
 }
 void update(LL val) //维护结点(tag值在下次访问再下放)
 {
  s+=val,sum+=(LL)(size)*val;
  if (size>1) tag+=val;
 }
 int cmp(int k)
 {
  if ((ch[0]->size+1)==k) return -1;
  return (ch[0]->size>=k)?(0):(1);
 }
} *root,*null;

Node *newnode(int v) {return new Node(v,null);}

//-----------------------------

Node *rotate(Node* &o,int d) 
{
 Node* k=o->ch[d^1];
 o->ch[d^1]=k->ch[d],o->ch[d^1]->f=o; //rotate需要维护父节点
 k->ch[d]=o,o->f=k;
 o->maintain(),k->maintain();o=k;
}

void splay(Node* &o,int k)
{
 o->pushdown();int d=o->cmp(k); //先下放
 if (d==1) k-=o->ch[0]->size+1;

 if (d!=-1)
 {
  Node* p=o->ch[d];p->pushdown();
  int d2=p->cmp(k),k2=(d2==0)?(k):(k-p->ch[0]->size-1);
  if (d2!=-1)
  {
   splay(p->ch[d2],k2);
   if (d==d2) rotate(o,d^1); else rotate(o->ch[d],d);
  }
  rotate(o,d^1);
 }
}

Node* merge(Node *x,Node *y)
{
 if (x==null) return y;
 if (y==null) return x;

 splay(x,x->size);
 x->ch[1]=y,y->f=x,x->maintain(); //维护父节点
 return x;
}

void split(Node *o,int k,Node* &l,Node* &r)
{
 if (k<=0) {l=null,r=o;return;}
 splay(o,k);
 l=o,r=o->ch[1],l->f=r->f=null,o->ch[1]=null,l->maintain(); //记得清空父节点
}

void rnk(Node *o,int &ans,int d) //调用时 ans=0,d=1
{
 if (o==null) return;
 if (d==1) ans+=o->ch[0]->size+1;
 rnk(o->f,ans,(o->f->ch[0]==o)?(0):(1));
}
posted @ 2017-02-28 15:30  Krew  阅读(184)  评论(0)    收藏  举报