[ZJOI2006]书架

题目描述

有n本书从上到下放,有5种操作:

1.将编号为s的书放在最上面

2.把编号为s的书放在最下面

3.把编号为s的书上下移动一个位置或者不动

4.询问编号为s的书上面的书的个数

5.从上面数第k个书的编号

100%的数据,n,m <= 80000

题解

序列的平衡树。前后插入最值方便些。下标为编号

1.在原序列上删除,再插入在第一个数之后。

2.在原序列上删除,再插入在第n个数(因为自己出来了,最开始又有最值)之后。

3.不动就不管。他在原序列的位置为pos,先删除。向上移动的话插入在第pos-2个数之后;向下移动插入在第pos个数之后(自己出来了)。

4.把s旋到根,输出左儿子的size-1(最值)

5.查询第k+1即可

需要注意的就是-1是向上(可能只有我搞错,蠢哭);求编号s的位置,不是直接左儿子的size+1(因为不在根,再次蠢哭);

#include<bits/stdc++.h>
using namespace std;

const int maxn=80005;
int n,m,root;
int a[maxn];
struct Splay{
  int fa,size,s[2];
}tr[maxn];

template<class T>inline void read(T &x){
  x=0;int f=0;char ch=getchar();
  while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
  while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  x= f ? -x : x ;
}

void update(int x){
  tr[x].size=tr[tr[x].s[0]].size+tr[tr[x].s[1]].size+1;
}

int build(int f,int l,int r){
  if(l>r) return 0;
  int mid=(l+r)>>1,now=a[mid];
  tr[now].fa=f;
  tr[now].s[0]=build(now,l,mid-1);
  tr[now].s[1]=build(now,mid+1,r);
  update(now);
  return now;
}

int find(int x){
  int now=root;
  while(1){
    if(tr[tr[now].s[0]].size>=x){now=tr[now].s[0];continue;}
    x-=tr[tr[now].s[0]].size;
    if(x==1) return now;
    x--;
    now=tr[now].s[1];
  }
}

int get(int x){return tr[tr[x].fa].s[1]==x;}

void connect(int x,int y,int d){
  tr[x].fa=y;
  tr[y].s[d]=x;
}

void rotate(int x){
  int f=tr[x].fa,ff=tr[f].fa;
  int d1=get(x),d2=get(f);
  int cs=tr[x].s[d1^1];
  connect(x,ff,d2);
  connect(f,x,d1^1);
  connect(cs,f,d1);
  update(f);
  update(x);
}

void splay(int x,int go){
  if(go==root) root=x;
  go=tr[go].fa;
  while(tr[x].fa!=go){
    int f=tr[x].fa;
    if(tr[f].fa==go) rotate(x);
    else if(get(x)==get(f)) {rotate(f);rotate(x);}
    else {rotate(x);rotate(x);}
  }
}

void del(int now){
  splay(now,root);
  int pos=tr[tr[now].s[0]].size+1;
  int x=find(pos-1),y=find(pos+1);
  splay(x,root);
  splay(y,tr[x].s[1]);
  tr[now].fa=0;
  tr[y].s[0]=0;
  update(y);
  update(x);
}

void put(int now,int pos){
  del(now);
  int x=find(pos),y=find(pos+1);
  splay(x,root);
  splay(y,tr[x].s[1]);
  tr[now].fa=y;
  tr[y].s[0]=now;
  update(y);
  update(x);
}

void insert(int now,int t){
  splay(now,root);
  int pos=tr[tr[now].s[0]].size+1;
  del(now);
  if(t==-1) pos-=2;
  int x=find(pos),y=find(pos+1);
  splay(x,root);
  splay(y,tr[x].s[1]);
  tr[y].s[0]=now;
  tr[now].fa=y;
  update(y);
  update(x);
}

void debug(int x){
  if(tr[x].s[0]) debug(tr[x].s[0]);
  if(x!=n+1&&x!=n+2) printf("%d ",x);
  if(tr[x].s[1]) debug(tr[x].s[1]);
}

int main(){
  read(n);read(m);
  for(int i=1;i<=n;i++) read(a[i+1]);
  a[1]=n+1;
  a[n+2]=n+2;
  root=build(0,1,n+2);
  for(int i=1;i<=m;i++){
    char op[10];
    scanf("%s",op);
    if(op[0]=='T'){
      int x;read(x);
      put(x,1);
    }
    else if(op[0]=='B'){
      int x;read(x);
      put(x,n);
    }
    else if(op[0]=='I'){
      int x,t;
      read(x);read(t);
      if(!t) continue;
      insert(x,t);
    }
    else if(op[0]=='A'){
      int x;read(x);
      splay(x,root);
      printf("%d
",tr[tr[x].s[0]].size-1);
    }
    else {
      int x;read(x);
      printf("%d
",find(x+1));
    }
    //putchar(10);
    //debug(root);
    //putchar(10);
  }
}
View Code

还有说树状数组或者权值线段树可以做的,数组要开3倍(只能%%%)。

原文地址:https://www.cnblogs.com/sto324/p/11380286.html