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;

}

  

 

你可能感兴趣的:(ZOJ)