标签:
Description
Input
Output
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; }
标签:
原文地址:http://www.cnblogs.com/wust-ouyangli/p/5746814.html