NC20463. [ZJOI2006]BOOK 书架
描述
输入描述
第一行有两个数n,m,分别表示书的个数以及命令的条数;
第二行为n个正整数:第i个数表示初始时从上至下第i个位置放置的书的编号;
第三行到m+2行,每行一条命令。命令有5种形式:
1. Top S——表示把编号为S的书房在最上面。
2. Bottom S——表示把编号为S的书房在最下面。
3. Insert S T——T∈{-1,0,1},若编号为S的书上面有X本书,则这条命令表示把这本书放回去后它的上面有X+T本书;
4. Ask S——询问编号为S的书的上面目前有多少本书。
5. Query S——询问从上面数起的第S本书的编号。
输出描述
对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。
示例1
输入:
10 10 1 3 2 7 5 8 10 4 9 6 Query 3 Top 5 Ask 6 Bottom 3 Ask 3 Top 6 Insert 4 -1 Query 5 Query 2 Ask 2
输出:
2 9 9 7 5 3
C++14(g++5.4) 解法, 执行用时: 101ms, 内存消耗: 2792K, 提交时间: 2020-04-01 19:29:13
#include<bits/stdc++.h> #define lson t[x].son[0] #define rson t[x].son[1] using namespace std; const int MX=1e5+9; const int head=1e5+1; const int tail=1e5+2; struct node{ int siz,val,fa; int son[2]; }t[MX]; int root,sz=0,pos[MX]; int n,m,val[MX]; char s[MX]; int get(int x){ return t[t[x].fa].son[1]==x; } void pushup(int x){ t[x].siz=1; if( lson ) t[x].siz+=t[lson].siz; if( rson ) t[x].siz+=t[rson].siz; } void rotate(int x){ int f=t[x].fa,ff=t[f].fa,kx=get(x),kf=get(f); t[ff].son[kf]=x,t[x].fa=ff; t[f].son[kx]=t[x].son[!kx],t[t[x].son[!kx]].fa=f; t[x].son[!kx]=f,t[f].fa=x; pushup(f),pushup(x); } void splay(int rt,int x){ while( t[x].fa!=rt ){ int f=t[x].fa,ff=t[f].fa; if( ff!=rt ){ if( get(x)==get(f) ) rotate(f); else rotate(x); } rotate(x); } if( rt==0 ) root=x; } int build(int l,int r,int fa){ if( l>r ) return 0; int mid=(l+r)>>1; int x=++sz; t[x].val=val[mid]; pos[t[x].val]=x; t[x].fa=fa; lson=rson=0; lson=build(l,mid-1,x); rson=build(mid+1,r,x); pushup(x); // 这个不可以忘记 return x; } int las(int x){ splay(0,x); x=rson; while( lson ) x=lson; return x; } int pre(int x){ splay(0,x); x=lson; while( rson ) x=rson; return x; } void Top(int x){ int last=las(x); splay(0,pos[head]); // splay只负责向上转,转到rt为止,一定要保证rt在x上面 splay(pos[head],x); splay(x,last); t[last].son[0]=lson; t[lson].fa=last; lson=0; pushup(last); pushup(x); } void Bottom(int x){ int pree=pre(x); splay(0,pos[tail]); splay(pos[tail],x); splay(x,pree); t[pree].son[1]=rson; t[rson].fa=pree; rson=0; pushup(pree); pushup(x); } int fin_order(int sz){ int x=root; while( x ){ if( sz<=t[lson].siz ) x=lson; else if( sz==t[lson].siz+1 ) return x; else{ sz-=(t[lson].siz+1); x=rson; } } } int main() { //freopen("input.txt","r",stdin); scanf("%d %d",&n,&m); val[0]=head,val[n+1]=tail; for( int i=1 ; i<=n ; i++ ) scanf("%d",&val[i]); root=build(0,n+1,0); char S[20]; int s,tt; while( m-- ){ scanf("%s",S); if( S[0]=='T' ){ scanf("%d",&s); Top(pos[s]); } else if( S[0]=='B' ){ scanf("%d",&s); Bottom(pos[s]); } else if( S[0]=='I' ){ scanf("%d %d",&s,&tt); if( tt!=0 ){ if( tt==1 ){ int lass=las(pos[s]),x=pos[s]; splay(0,pos[s]); splay(pos[s],lass); t[lass].son[0]=lson,t[lson].fa=lass; lson=0; rson=t[lass].son[1],t[t[lass].son[1]].fa=x; t[lass].son[1]=x,t[x].fa=lass; t[lass].fa=0,root=lass; pushup(x); pushup(lass); } else{ int pree=pre(pos[s]),x=pos[s]; splay(0,pos[s]); splay(pos[s],pree); t[pree].son[1]=rson,t[rson].fa=pree; rson=0; lson=t[pree].son[0],t[t[pree].son[0]].fa=x; t[pree].son[0]=x,t[x].fa=pree; t[pree].fa=0,root=pree; pushup(x); pushup(pree); } } } else if( S[0]=='A' ){ scanf("%d",&s); splay(0,pos[s]); printf("%d\n",t[t[root].son[0]].siz-1); } else{ scanf("%d",&s); printf("%d\n",t[fin_order(s+1)].val); } } return 0; }
C++ 解法, 执行用时: 113ms, 内存消耗: 3320K, 提交时间: 2022-03-19 14:30:20
#include <iostream> #include <cstdio> #include <cstring> #include <ctime> #include <cstdlib> using namespace std; const int N=2e5+10; int read() { int x=0,f=0,c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return f?-x:x; } int sz[N],lch[N],rch[N],rnd[N],val[N],pos[N],fa[N]; int root,rx,ry,rz,cnt; int n,m; void SWAP(int x,int y) { swap(val[x],val[y]); pos[val[x]]=x; pos[val[y]]=y; } int _new(int x){sz[++cnt]=1; rnd[cnt]=rand(); val[cnt]=x; pos[x]=cnt; return cnt;} void upd(int x){ sz[x]=sz[lch[x]]+sz[rch[x]]+1;} //记录父亲的做法 void split(int now,int k,int &x,int &y,int fax,int fay) { if(!now){ x=y=0; return;} if(k>=sz[lch[now]]+1){ x=now; fa[x]=fax; split(rch[now],k-sz[lch[now]]-1,rch[x],y,x,fay);} else {y=now; fa[y]=fay; split(lch[now],k,x,lch[y],fax,y);} upd(now); } int merge(int x,int y) { if(!x||!y) return x+y; if(rnd[x]<rnd[y]){ rch[x]=merge(rch[x],y); fa[rch[x]]=x; upd(x); return x;} else { lch[y]=merge(x,lch[y]); fa[lch[y]]=y; upd(y); return y;} } //需要获取编号为x的书本的位置 //但是平衡树中不记录下标 //但是我们只需要在logn的时间里获得位置即可 因此暴力向上跳就可以 int findp(int x,int rt) { int p=pos[x],ret=0; ret+=sz[lch[p]]+1; while(p!=rt) { if( rch[fa[p]]==p ) ret+=sz[lch[fa[p]]]+1; p=fa[p]; } return ret; } //调试的时候不要拿完全二叉树来调试 //向着左上方向走的才会被记入答案中 int findk(int k,int rt) { int now=rt; while(1) { int lsz=sz[lch[now]],rsz=sz[rch[now]]; if(k==lsz+1) return now; else if(k<lsz+1) now=lch[now]; else now=rch[now],k=k-lsz-1; } } void print(int x) { if(!x) return; cout<<x<<" "<<lch[x]<<" "<<rch[x]<<endl; print(lch[x]); // cout<<val[x]<<" "; print(rch[x]); } void Top(int x) { // print(root); puts(""); int p=findp(x,root); split(root,p,rx,rz,0,0); split(rx,p-1,rx,ry,0,0);//连续分裂的根的问题 root=merge( merge(_new(x),rx),rz ); // print(root); puts(""); // cout<<sz[root]<<endl; } void Bottom(int x) { // print(root); puts(""); int p=findp(x,root); split(root,p,rx,rz,0,0); split(rx,p-1,rx,ry,0,0); root=merge( merge(rx,rz),_new(x) ); // print(root);puts(""); } void Insert(int x,int t) { if(t==0) return; int p=findp(x,root); int q=findk(p+t,root); SWAP(pos[x],q); // print(root); } int main() { // freopen("data.txt","r",stdin); srand(1); n=read(); m=read(); for(int i=1;i<=n;i++) { int x=read(); root=merge(root,_new(x)); } // print(root); puts(""); for(int i=1;i<=m;i++) { char s[10]; scanf("%s",s); char c=s[0]; int x=read(); if(c=='T') Top(x); else if(c=='B') Bottom(x); else if(c=='I') Insert(x,read()); else if(c=='A') printf("%d\n",findp(x,root)-1); else printf("%d\n",val[findk(x,root)]); // print(root); puts(""); } }