BZOJ3595 : [Scoi2014]方伯伯的Oj

由于n很大,有2e8,所以不能直接用splay来维护排名

把splay修改一下

每个节点维护一个区间[l,r],表示编号在[l,r]之间的所有点都在这里

需要支持一个takeout操作:

把编号为k的玩家分离出来,成为一个独立的点

先找到它所在的大点x

splay(x)

然后分裂成1-3个节点

关于如何查找编号为k的玩家在splay中哪个节点

可以开一棵动态开节点的线段树来维护

每次分裂实质就是区间赋值,打标记即可

时间复杂度$O(m\log n)$

 

写起来真是神清气爽…

 

#include<cstdio>
#define N 300010
#define M 9000000
const int R=200000000;
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
int ans;
struct Segmenttree{
int tot,l[M],r[M],tag[M],val[M];
inline void make1(int x,int a,int b,int p){
  if(!x)return;
  if(a==b)val[x]=p;else tag[x]=p;
}
inline void pb(int x,int a,int b){
  if(tag[x]){
    int mid=(a+b)>>1;
    if(!l[x])l[x]=++tot;
    if(!r[x])r[x]=++tot;
    make1(l[x],a,mid,tag[x]);
    make1(r[x],mid+1,b,tag[x]);
    tag[x]=0;
  }
}
void change(int x,int a,int b,int c,int d,int p){
  if(c<=a&&b<=d){
    make1(x,a,b,p);
    return;
  }
  int mid=(a+b)>>1;
  pb(x,a,b);
  if(c<=mid){
    if(!l[x])l[x]=++tot;
    change(l[x],a,mid,c,d,p);
  }
  if(d>mid){
    if(!r[x])r[x]=++tot;
    change(r[x],mid+1,b,c,d,p);
  }
}
int ask(int x,int a,int b,int c){
  if(a==b)return val[x];
  int mid=(a+b)>>1;
  pb(x,a,b);
  return c<=mid?ask(l[x],a,mid,c):ask(r[x],mid+1,b,c);
}
inline void init(){
  tot=1;
  make1(1,1,R,1);
}
}S;
int tot,root,f[N],son[N][2],l[N],r[N],sum[N];
inline void init(int n){
  tot=root=l[1]=1;r[1]=n;
}
inline void up(int x){sum[x]=sum[son[x][0]]+sum[son[x][1]]+r[x]-l[x]+1;}
inline void setson(int x,int w,int y){son[x][w]=y;if(y)f[y]=x;}
inline void rotate(int x){
  int y=f[x],w=son[y][1]==x;
  son[y][w]=son[x][!w];
  if(son[x][!w])f[son[x][!w]]=y;
  if(f[y]){
    int z=f[y];
    if(son[z][0]==y)son[z][0]=x;
    if(son[z][1]==y)son[z][1]=x;
  }
  f[x]=f[y];f[y]=x;son[x][!w]=y;up(y);
}
inline void splay(int x){
  while(f[x]){
    int y=f[x];
    if(f[y]){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
    rotate(x);
  }
  up(root=x);
}
inline int kth(int k){
  int x=root,nl,nr;
  while(1){
    nl=sum[son[x][0]]+1;nr=nl+r[x]-l[x];
    if(nl<=k&&k<=nr)return k-nl+l[x];
    if(k<nl)x=son[x][0];
    else k-=nr,x=son[x][1];
  }
}
inline int takeout(int k){//将编号为k的点分离成单点
  int x=S.ask(1,1,R,k);
  splay(x);
  int tl=l[x],tr=r[x],sl=son[x][0],sr=son[x][1];
  l[x]=r[x]=k;
  if(k!=tl){
    int y=++tot;
    l[y]=tl;r[y]=k-1;
    setson(y,0,sl);
    up(y);
    S.change(1,1,R,tl,k-1,y);
    setson(x,0,y);
  }else setson(x,0,sl);
  if(k!=tr){
    int y=++tot;
    l[y]=k+1;r[y]=tr;
    setson(y,1,sr);
    up(y);
    S.change(1,1,R,k+1,tr,y);
    setson(x,1,y);
  }else setson(x,1,sr);
  up(x);
  ans=sum[son[x][0]]+1;
  return x;
}
inline void top(int k){//把编号为k的点放在首位
  int x=takeout(k),a=son[x][0],b=son[x][1],i;
  if(b){
    f[b]=0;
    i=b;
    while(son[i][0])i=son[i][0];
    splay(i);
    setson(i,0,a);
    up(i);
  }else root=a;
  son[x][0]=0;
  setson(x,1,root);
  up(root=x);
}
inline void bottom(int k){//把编号为k的点放在末尾
  int x=takeout(k),a=son[x][0],b=son[x][1],i;
  if(b){
    f[b]=0;
    i=b;
    while(son[i][0])i=son[i][0];
    splay(i);
    setson(i,0,a);
    up(i);
  }else root=a;
  son[x][1]=0;
  setson(x,0,root);
  up(root=x);
}
inline void change(int k,int p){//把编号为k的点的编号改为p
  int x=takeout(k);
  l[x]=r[x]=p;
  S.change(1,1,R,p,p,x);
}
int n,m,k,x,y;
int main(){
  read(n);read(m);
  init(n);
  S.init();
  while(m--){
    read(k);read(x);x-=ans;
    if(k==1){
      read(y);y-=ans;
      change(x,y);
      printf("%d\n",ans);
    }
    if(k==2)top(x),printf("%d\n",ans);
    if(k==3)bottom(x),printf("%d\n",ans);
    if(k==4)printf("%d\n",ans=kth(x));
  }
  return 0;
}

  

 

posted @ 2014-05-18 23:20  Claris  阅读(404)  评论(0编辑  收藏  举报