Hdu3436-Queue-jumpers(伸展树)
Description
Ponyo and Garfield are waiting outside the box-office for their favorite movie. Because queuing is so boring, that they want to play a game to kill the time. The game is called “Queue-jumpers”. Suppose that there are N people numbered from 1 to N stand in a line initially. Each time you should simulate one of the following operations: 1. Top x :Take person x to the front of the queue 2. Query x: calculate the current position of person x 3. Rank x: calculate the current person at position x Where x is in [1, N]. Ponyo is so clever that she plays the game very well while Garfield has no idea. Garfield is now turning to you for help.
Input
In the first line there is an integer T, indicates the number of test cases.(T<=50) In each case, the first line contains two integers N(1<=N<=10^8), Q(1<=Q<=10^5). Then there are Q lines, each line contain an operation as said above.
Output
For each test case, output “Case d:“ at first line where d is the case number counted from one, then for each “Query x” operation ,output the current position of person x at a line, for each “Rank x” operation, output the current person at position x at a line.
Sample Input
3 9 5 Top 1 Rank 3 Top 7 Rank 6 Rank 8 6 2 Top 4 Top 5 7 4 Top 5 Top 2 Query 1 Rank 6
Sample Output
Case 1: 3 5 8 Case 2: Case 3: 3 6
题意:刚开始给出一个N,代表初始是1,2,3...N排成一行,有三种操作 Top x 将值x置于最前面 Query x 查询值x排在第几 Rank x 查询排在第x的位置是数值几
解析:这道题坑了我半天,一直超时,看了别人的博客,原来是自己 的Splay写low了,而且要加一个地方才能不超时,我代码中有注释。 数据达到10^8,显然不能建这么大一颗树,需要离散化,把Top和Query操作 的数保存下来离散化,然后处理出一段段区间 一开始按照区间段排在第几个的位置建树,要保存区间段v对应的是哪个节点的 编号,如果是Top操作,先二分找到x对应的区间k,将k对应的节点伸展到根,然后 删除这个节点,再插入最右边,如果是Query操作,也是二分找到x对应的区间k, 将k对应的节点伸展到根,即可得到他的排名。如果是Rank操作,从根开始找就行, 判断是否在某一段区间内。然后找到了就可以得到答案了。
代码
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define t tr[r] #define y tr[fa] const int maxn=200005; int N,Q,A[maxn];//A数组用于保存离散化的数 char op[maxn][7]; //操作字符串 int opx[maxn],seg[maxn][2],Size;//操作值,区间左右端点,多少个区间 int BIS(int v) //二分找区间下标 { int x=1,yy=Size,mid; while(x<=yy) { int mid=(x+yy)/2; if(seg[mid][0]<=v&&v<=seg[mid][1]) return mid; //找到了 if(seg[mid][1]<v) x=mid+1; else yy=mid; } } int bel[maxn]; //用于保存第几段区间现在的节点编号 struct treap { int son[2],fa; //左右儿子和父亲 int s,v,num; //s是大小,v代表区间的下标,num代表这段区间的大小 treap(){ s=v=num=0; son[0]=son[1]=fa=0; } int rk();//排名 void pushup(); //更新 int cmp(int k); //比较 }tr[maxn*2]; int treap::rk(){ return tr[son[0]].s+num; } void treap::pushup(){ s=tr[son[0]].s+tr[son[1]].s+num; } int treap::cmp(int k) { if(tr[son[0]].s<k&&k<=rk()) return -1;//在区间内 if(k<rk()) return 0; else return 1; } struct splaytree { int id,root; void init(){ id=root=0; tr[0].s=tr[0].num=0; }//初始化 void dfs(int r) { if(r==0) return; dfs(t.son[0]); printf("%d ",t.v); dfs(t.son[1]); } void Visit(){ dfs(root); puts("");} int NewNode(int fa,int v) //得到新节点 { bel[v]=++id; //保存v值对应的下标 int r=id; t.fa=fa; t.v=v; t.s=t.num=seg[v][1]-seg[v][0]+1; t.son[0]=t.son[1]=0; return r; } void Build(int &r,int le,int ri,int fa)//建树 { if(le>ri) return; //区间不存在 int mid=(le+ri)/2; r=NewNode(fa,mid); //得到新节点 Build(t.son[0],le,mid-1,r); //左建树 Build(t.son[1],mid+1,ri,r); //右建树 t.pushup(); } void Insert(int &r,int fa,int k) //插入到最左边 { if(r==0){ r=NewNode(fa,k); return; } //在最左边建立新节点 Insert(t.son[0],r,k); t.pushup(); } int GetMin(int r) //得到这棵子树最左端的节点 { while(t.son[0]!=0) r=t.son[0]; return r; } void Rotate(int r,int d) //翻转 { int fa=t.fa; y.son[d^1]=t.son[d]; if(t.son[d]!=0) tr[t.son[d]].fa=fa; if(y.fa==0) t.fa=0; else if(tr[y.fa].son[0]==fa) { t.fa=y.fa; tr[t.fa].son[0]=r; } else if(tr[y.fa].son[1]==fa) { t.fa=y.fa; tr[t.fa].son[1]=r; } t.son[d]=fa; y.fa=r; y.pushup(); t.pushup(); } void Splay(int r) //伸展 { while(t.fa!=0) { if(tr[t.fa].fa==0) Rotate(r,tr[t.fa].son[0]==r); else { int fa=t.fa; int d=(tr[y.fa].son[0]==fa); if(y.son[d]==r) { Rotate(r,d^1); Rotate(r,d); } else { Rotate(fa,d); Rotate(r,d); } } } } void Top(int &r,int k) //将k对应的区间置于最前面 { r=bel[k]; Splay(r);//伸展到根 int rr=GetMin(t.son[1]);//找到右子树最小的节点 Splay(rr); //伸展到根 treap& tt=tr[rr]; tt.son[0]=t.son[0]; //将原来的左子树连到rr上去 if(t.son[0]!=0) tr[t.son[0]].fa=rr; tt.fa=0; tt.pushup(); r=rr; Insert(r,0,k); //插入到最右边 Splay(r=id); //不加这个会超时。。。 } int Query(int &r,int k) { r=bel[k]; Splay(r); return t.rk(); } int Rank(int &r,int k) { int d=t.cmp(k); if(d==-1) return seg[t.v][0]+ k- tr[t.son[0]].s - 1; if(d==0) return Rank(t.son[0],k); else return Rank(t.son[1],k- t.rk()); } }spt; int main() { int T,Case=0; scanf("%d",&T); while(T--) { scanf("%d%d",&N,&Q); int k=0; A[k++]=0; for(int i=0;i<Q;i++) { scanf("%s%d",op[i],&opx[i]); if(op[i][0]=='T'||op[i][0]=='Q') A[k++]=opx[i]; } A[k++]=N+1; sort(A,A+k); Size=0; for(int i=1;i<k;i++) //处理出每段区间 { if(A[i]==A[i-1]) continue; if(A[i]-A[i-1]>1){ seg[++Size][0]=A[i-1]+1; seg[Size][1]=A[i]-1; } seg[++Size][0]=A[i]; seg[Size][1]=A[i]; } spt.init(); spt.Build(spt.root,1,Size,0); printf("Case %d:\n",++Case); for(int i=0;i<Q;i++) { if(op[i][0]=='T') spt.Top(spt.root,BIS(opx[i])); else if(op[i][0]=='Q') printf("%d\n",spt.Query(spt.root,BIS(opx[i]))); else printf("%d\n",spt.Rank(spt.root,opx[i])); } } return 0; }