splay的基本操作之点删除,区间提取

mid
维护一个集合,有2种操作:
1.每次可以插入一个元素。
2.找出当前集合中第(n+1)/2大的元素,把它输出,然后把它从集合中删除。
N<=100000
输入
第1行一个数N,表示由N次操作。接下来N行。每行第1个数C表示操作类型。
1.c=1,接下来还有一个数,表示要插入元素。
2.c=2,表示要进行第2种操作。
输出
一共m行。按顺序对应输入中的第2种情况。
样例输入 Copy
5
1 3
2
1 5
1 4
2
样例输出 Copy
3
5

查找:与二叉排序树查找类似,只是查找结束后要将找到的元素通过Splay操作旋转到根。
插入:用查找过程找到要插入的位置,进行插入。然后将新元素旋转到根。
删除:首先在树中找到要删除的元素x,将他转到根节点并删去,这样原来的树就分裂成了两棵树,然后将左子树中拥有最大关键字的那一个元素p转到根,由于它是左子树中的最大元素,所以他不存在右儿子,这样只要把原来x的右子树作为p的右子树,就重新合并成了一棵树。

 

#include<bits/stdc++.h>
using namespace std;
int fa[100010],son[100010][2],size[100010],val[100010],n,k,pps,root,tot;
int check(int x)
{
     if(son[fa[x]][1]==x)
	    return 1;
	 else 
	    return 0;
}
void down(int x)
{
    size[x]=size[son[x][0]]+size[son[x][1]]+1;
}
int pre(int x)
{
    int now=son[x][0];
    while(son[now][1])now=son[now][1];
    return now;
}
void move(int x)
{
    int f=fa[x],s=son[x][check(x)^1],ret=check(x);
    son[x][ret^1]=f,son[f][ret]=s;
    if(s)fa[s]=f;fa[x]=fa[f];
    if(fa[x])son[fa[x]][check(f)]=x;
    fa[f]=x;
    down(f);
	down(x);
}
void splay(int x)
{
    while(fa[x])
    {
        if(fa[fa[x]])
        {
            if(check(fa[x])==check(x))move(fa[x]);
            else move(x);
        }move(x);
    }
	root=x;
}
void insert(int x)
{
    val[++tot]=x;int now=root;size[tot]=1;
    if(!root){root=tot;return;}
    while(true)
    {
        if(x<=val[now])
        {
            if(!son[now][0]){son[now][0]=tot,fa[tot]=now;down(now);break;}
            now=son[now][0];
        }
        else
        {
            if(!son[now][1]){son[now][1]=tot,fa[tot]=now;down(now);break;}
            now=son[now][1];
        }
    }
	splay(tot);
}
void del(int x)
{
    splay(x);
	int p=pre(x);
	//如果x的左子树中的最大点 
    if(!p)
    //如果左子树为空 
    {
        root=son[x][1];
        //则右子树为根 
        fa[son[x][1]]=0;
        return;
    }
    splay(p);
	//root=p;这一句多余的吧。
    //否则将p旋转至根 
    if(son[x][1])
    //如果从前x有右子结点,则它做为P的右子结点 
	   fa[son[x][1]]=p;
    son[p][1]=son[x][1];
    son[x][0]=son[x][1]=fa[x]=fa[p]=0;
    down(p);
	down(x);
}
void find(int x)
{
    int now=root;
    while(true)
    {
        if(size[son[now][0]]+1==x){printf("%d\n",val[now]),del(now);return;}
        if(size[son[now][0]]+1>x&&son[now][0]>0)now=son[now][0];
        else x=x-size[son[now][0]]-1,now=son[now][1];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&k);
        if(k==1)
        {
            scanf("%d",&pps);
            insert(pps);
        }
        else find(size[root]-(size[root]+1)/2+1);
    }
}

 

  

 

var
s:array[-10..500000,0..1]of longint;
v,f,c:array[-10..500000]of longint;
root,len:longint;
  
Function t(x:longint):longint;  //求出x是其父亲的左儿子还是右儿子
begin
  if s[f[x],1]=x then     exit(1);  exit(0);
end;
  
Procedure down(x:longint);  //统计以x为根的树中有多少个结点,包括他自己
begin
  if x=0 then     exit;
  c[x]:=c[s[x,1]]+c[s[x,0]]+1;
end;
  
Procedure move(x:longint); //一次的旋转操作
var
fa,son:longint;
begin
  fa:=f[x];
  son:=s[x,1-t(x)];
  s[x,1-t(x)]:=fa;
  s[fa,t(x)]:=son;
  if son>0 then  
     f[son]:=fa;
  f[x]:=f[fa];
  if f[fa]>0 then 
     s[f[fa],t(fa)]:=x;
  f[fa]:=x;
  down(fa);
  down(x);
end;
  
Procedure splay(x:longint);
begin
while f[x]>0 do
  begin
    if f[f[x]]>0 then    //如果x的父亲,还有父亲的话  
       if t(x)=t(f[x]) then  //如果x与x的父亲及其祖先点在一条直线上,而先旋转x的父亲
           move(f[x])
       else  
          move(x);     //否则说明是个之字形结构,先旋转x本身
    move(x);
  end;
root:=x;
end;
Procedure join(x:longint);
var
i:longint;
begin
  Inc(len);
  v[len]:=x;
  if root=0 then
  begin
    root:=len;
    f[len]:=0;
    s[len,1]:=0;
    s[len,0]:=0;
    exit;
  end;
  i:=root;
  while true do
    if x<=v[i] then
    begin
      Inc(c[i]);
      if s[i,0]=0 then
      begin
        s[i,0]:=len;
        f[len]:=i;
        break;
      end;
      i:=s[i,0];
    end
    else
    begin
      Inc(c[i]);
      if s[i,1]=0 then
      begin
        s[i,1]:=len;
        f[len]:=i;
        break;
      end;
      i:=s[i,1];
    end;
splay(len);
end;
  
Procedure Del(x:longint);
var
i,son:longint;
begin
  splay(x);
  if (s[x,0]=0) and (s[x,1]=0) then 
//如果左右儿子均为空,则删除当前点后,成为一个空树
  begin
    root:=0;
    exit;
  end;
  for i:=0 to 1 do  //看左右儿子哪个为空,则另一个儿子做根结点
    if s[x,i]=0 then
    begin
      root:=s[x,1-i];
      f[root]:=0;
      exit;
    end;
  i:=s[x,0];     //取出x左儿子
  while s[i,1]>0 do       //不断去找右儿子
     i:=s[i,1];
  splay(i);      //将这个点做为根
  son:=s[x,1];   //取出从前那个点的右儿子
  s[i,1]:=son;   //将其做为i这个点的右儿子
  f[son]:=i;
  down(i);
end;
  
Function find(x:longint):longint;
var
i:longint;
begin
  i:=root;
  while true do
  begin
    if c[s[i,0]]+1=x then  
        exit(i);
    if (c[s[i,0]]+1>x) and (s[i,0]>0) then 
        i:=s[i,0]
    else
    begin
      x:=x-(c[s[i,0]]+1);
      i:=s[i,1];
    end;
  end;
end;
  
Procedure main;
var
i,n,k,x,l:longint;
begin
  readln(n);
  root:=0; len:=0;
  for i:=1 to n do
  begin
    read(k);
    if k=1 then
    begin
      readln(x);
      join(x);
      continue;
    end
    else
    begin
      l:=c[root]-(c[root]+1) div 2+1;//转成求第l小的元素
      l:=find(l);
      writeln(v[l]);
      Del(l);
    end;
  end;
end;
  
begin
  main;
end.

  

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100050;
int n;
int read(){
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
struct Splay
{
    int tot,root;
    int fa[maxn],son[maxn][2],val[maxn],siz[maxn];
    bool t(int node)
	{
	      return son[fa[node]][1]==node;
	}
    void updata(int node)
    {
	    siz[node]=siz[son[node][0]]+1+siz[son[node][1]];
	}
    void move(int node)
	{
        int ret=t(node),f=fa[node],s=son[node][ret^1];
        son[f][ret]=s;
		if(s)
		   fa[s]=f;
		son[node][ret^1]=f;
        fa[node]=fa[f];
		if(fa[f])
		   son[fa[f]][t(f)]=node;
        fa[f]=node;
		updata(f);
		updata(node);
    }
    void splay(int node)
	{
        while(fa[node])
		{
            if(fa[fa[node]])
			{
                if(t(fa[node])==t(node))
				    move(fa[node]);
                else
				     move(node);
            }
            move(node);
        }
        root=node;
    }
    void insert(int v)
	{
        val[++tot]=v;siz[tot]=1;
        if(!root){root=tot;return;}
        int node=root;
        while(1){
            if(val[node]>=v)
			{
                if(!son[node][0])
				  {
				        son[node][0]=tot;
						fa[tot]=node;
						break;
				  }
                node=son[node][0];
            }else{
                if(!son[node][1]){son[node][1]=tot;fa[tot]=node;break;}
                node=son[node][1];
            }
        }
        splay(tot);
    }
    void destroy(int u)
	{
	      val[u]=son[u][0]=son[u][1]=siz[u]=fa[u]=0;
	}
    void del(int node)
	{
        splay(node);
        if(!son[node][0]&&!son[node][1])
		 {
		      destroy(node);
			  root=0;
			  return;
		 }
        if(!son[node][0]) //如果左结点为空 
		{
		   root=son[node][1],fa[root]=0;
		   //则右结点为根 
		   updata(root);
		   destroy(node);
		   return;
		}
        if(!son[node][1])
		{
	      root=son[node][0],fa[root]=0;
		  updata(root);destroy(node);
		  return;
		}
        fa[son[node][0]]=0;
		int s=son[node][0];
		//走到node的左结点 
		while(son[s][1])
		//不断向右找 
		    s=son[s][1];
		splay(s);
		//将s做为根 
        son[root][1]=son[node][1];
        //将从前node的右结点做为新根的右结点 
		siz[root]+=siz[son[node][1]];
		fa[son[root][1]]=root;
		destroy(node);
    }
    void find(int rk)
	{
        int node=root;
        while(1)
		{
            if(siz[son[node][1]]+1==rk)
			{
                printf("%d\n",val[node]);
                del(node);
				return;
            }
            if(siz[son[node][1]]+1>=rk)
			   node=son[node][1];
            else 
			    rk-=siz[son[node][1]]+1,node=son[node][0];
        }
    }
}T;
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        int mode=read()-1;
        if(mode)T.find((T.siz[T.root]+1)/2);
        else{int x=read();T.insert(x);}
    }
    return 0;
}

  区间提取:

区间操作:
比如我们要提取区间[a,b],那么我们将a前面一个数对应的结点转到树根,将b 后面一个结点对应的结点转到树根的右边,那么根右边的左子树就对应了区间[a,b]。
原因很简单,将a 前面一个数对应的结点转到树根后, a 及a 后面的数就在根的右子树上,然后又将b后面一个结点对应的结点转到树根的右边,那么[a,b]这个区间就是下图中B所示的子树。

 

posted @ 2020-10-14 16:22  我微笑不代表我快乐  阅读(402)  评论(0编辑  收藏  举报