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; }