[BZOJ1861][ZJOI2006]书架(平衡树splay)
来源:互联网 发布:通信线路迁改优化 编辑:程序博客网 时间:2024/05/01 04:20
题目描述
传送门
题解
splay模板题(笑
对于基础算法这个熟练程度怎么去省选啊= =
题目中所给的编号即为平衡树中的点权。
开一个数组记录一下权值为i的在树中的变化是多少,每次询问的时候直接找。
对于Top操作:
如果树里只有一个点,什么都不用干;
否则,
首先把这个点删除,
然后找到树中的第一个点,
将这个点转到根,
将删除的那个点插到根的右儿子;
对于Bottom操作:
如果树里只有一个点,什么都不用干;
否则,
首先把这个点删除,
然后找到树中的第n-1个点,
(注意删除了之后树里只有n-1个点,不能寻找第n个点!)
将这个点转到根,
将删除的那个点插到根的右儿子;
对于Insert操作:
0的话什么都不用干;
如果是1或-1的话,
首先要判断这个点是否已经在树的最后一个和第一个,是的话什么都不用干,
否则,
将其中的一个转到根,另一个转到根的一个儿子,
交换这两个点,
注意这里会有一大堆鬼畜的父子关系变化(具体看代码)
(hxy神犇只find了之后交换了两个点的权值,Orz这种优越的姿势)
对于Ask操作:
将查询的这个点转到根,
返回根的左儿子的大小即为答案
对于Query操作:
做一个find就好啦!
具体见代码,有很详细的注释。
代码
#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int max_n=800005;const int max_N=max_n*2;int n,m,s,t;char opt[10];int a[max_n],k[max_n],loc[max_n];int ch[max_N][2],f[max_N],size[max_N],key[max_N];int root,sz;inline void clear(int x){ ch[x][0]=ch[x][1]=f[x]=size[x]=key[x]=0;}inline int get(int x){ return ch[f[x]][1]==x;}inline void update(int x){ if (x){ size[x]=1; if (ch[x][0]) size[x]+=size[ch[x][0]]; if (ch[x][1]) size[x]+=size[ch[x][1]]; }}inline void rotate(int x){ int old=f[x],oldf=f[old],which=get(x); ch[old][which]=ch[x][which^1]; f[ch[old][which]]=old; ch[x][which^1]=old; f[old]=x; f[x]=oldf; if (oldf) ch[oldf][ch[oldf][1]==old]=x; update(old); update(x);}inline void splay(int x,int tar){ for (int fa;(fa=f[x])!=tar;rotate(x)) if (f[fa]!=tar) rotate((get(x)==get(fa))?fa:x); if (!tar) root=x;}inline int build(int l,int r,int fa){ if (l>r) return 0; int mid=(l+r)>>1; int now=++sz; //每个点的权值为每个点的编号,loc记录每个编号在树中的位置 key[now]=k[mid]; loc[k[mid]]=now; f[now]=fa; int lch=build(l,mid-1,now); int rch=build(mid+1,r,now); ch[now][0]=lch; ch[now][1]=rch; update(now); return now;}inline int find(int x){ int now=root; while (1){ if (ch[now][0]&&x<=size[ch[now][0]]) now=ch[now][0]; else{ int temp=1; if (ch[now][0]) temp+=size[ch[now][0]]; if (x==temp) return now; x-=temp; now=ch[now][1]; } }}inline int pre(){ int now=ch[root][0]; while (ch[now][1]) now=ch[now][1]; return now;}inline int next(){ int now=ch[root][1]; while (ch[now][0]) now=ch[now][0]; return now;}inline void del(int x){ splay(x,0); if (!ch[root][0]&&!ch[root][1]){ clear(root); root=0; return; } if (!ch[root][0]){ int oldroot=root; root=ch[oldroot][1]; f[root]=0; clear(oldroot); return; } if (!ch[root][1]){ int oldroot=root; root=ch[oldroot][0]; f[root]=0; clear(oldroot); return; } int leftbig=pre(),oldroot=root; splay(leftbig,0); ch[root][1]=ch[oldroot][1]; f[ch[oldroot][1]]=root; clear(oldroot); update(root);}inline void Swap(int x,int y,int opt){ //这里x表示的是要换到根的东西,y表示的是根 //交换两个点的size swap(size[x],size[y]); //lchx和rchx表示的是换到根的东西的两个儿子 int lchx=ch[x][0]; int rchx=ch[x][1]; //lchy和rchy表示的是根的两个儿子 int lchy=ch[y][0]; int rchy=ch[y][1]; //先将两个东西的两个儿子交换一下 ch[x][0]=lchy; ch[x][1]=rchy; ch[y][0]=lchx; ch[y][1]=rchx; //现在的根x的opt指向的那个儿子改为原来的根y ch[x][opt]=y; //原来的根y的父亲改为现在的根x,现在的根x的父亲为0 f[y]=x; f[x]=0; //原来的根的另一个儿子指向现在的根 if (opt==0) f[rchy]=x; else f[lchy]=x; //原来的换成根的那个东西的两个儿子的父亲改成根 f[lchx]=f[rchx]=y; root=x;}int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) scanf("%d",&k[i]); //k表示每一个点的编号,在树中的权值 root=build(1,n,0); for (int i=1;i<=m;++i){ scanf("%s",opt); scanf("%d",&s); switch(opt[0]){ case 'T':{ if (size[root]==1) continue; //num为权值为s的点在树中的位置 int num=loc[s]; //删除这个点 del(num); //find找树中位置第几个的点的编号,aa为第一个点的编号 int aa=find(1); //将第一个点转到根 splay(aa,0); //将当前点插到根的左子树 ch[root][0]=++sz; f[sz]=root; size[sz]=1; key[sz]=s; loc[s]=sz; update(root); break; } case 'B':{ if (size[root]==1) continue; int num=loc[s]; del(num); //因为删去了一个,所以应该为n-1个 int aa=find(n-1); //将第n-1个点转到根 splay(aa,0); //将当前点插到根的右子树 ch[root][1]=++sz; f[sz]=root; size[sz]=1; key[sz]=s; loc[s]=sz; update(root); break; } case 'I':{ if (size[root]==1) continue; scanf("%d",&t); if (!t) continue; if (t==-1){ int num=loc[s]; //将当前点转到根 splay(num,0); //如果没有左儿子,即它为序列的第一个,什么都不用干 if (!ch[root][0]) continue; //将当前点的前驱转到根的左儿子 splay(pre(),num); //交换根和根的左儿子 Swap(ch[root][0],root,0); } else{ int num=loc[s]; //将当前点转到根 splay(num,0); //如果没有右儿子,即它为序列的最后一个,什么都不干 if (!ch[root][1]) continue; //将当前点的后继转到根的右儿子 splay(next(),num); //交换根和根的右儿子 Swap(ch[root][1],root,1); } break; } case 'A':{ int num=loc[s]; //将当前点转到根 splay(num,0); //根的左儿子的大小即为前面有多少个 printf("%d\n",size[ch[root][0]]); break; } case 'Q':{ //aa为第s本书的编号 int aa=find(s); //输出这个编号对应的权值 printf("%d\n",key[aa]); break; } } }}
总结
这道题给我最重要的经验是父子关系的改变:
改变某两个点,有影响的点要向上向下都扩展一层,即离他们远一层的父亲和儿子;
这两个点的父子关系也要发生变化;
如果与根有牵扯,最后要确定根。
0 0
- [BZOJ1861][ZJOI2006]书架(平衡树splay)
- [BZOJ1861][Zjoi2006]Book 书架 && splay
- 【BZOJ1861】【Zjoi2006】Book 书架 Splay
- 【bzoj1861】[Zjoi2006]Book 书架 splay
- BZOJ1861: [Zjoi2006]Book 书架 Splay
- [bzoj1861][Zjoi2006]Book书架 splay
- 【BZOJ1861】书架(Splay)
- 洛谷P2596 [ZJOI2006]书架(BZOJ1861)
- [BZOJ1861][Zjoi2006]Book 书架
- [BZOJ1861] [Zjoi2006]Book 书架
- bzoj1861: [Zjoi2006]Book 书架
- BZOJ1861: [Zjoi2006]Book 书架
- 【ZJOI2006】bzoj1861 书架
- bzoj1861: [Zjoi2006]Book 书架
- bzoj1861: [Zjoi2006]Book 书架
- bzoj1861 [Zjoi2006]Book 书架
- luoguP2596 [ZJOI2006]书架(splay)
- bzoj1861 book书架 splay
- 二叉树--前序遍历、中序遍历、后序遍历、层次遍历
- mysql5.7.11修改root密码
- 欢迎使用CSDN-markdown编辑器
- 随机十个数的和 九种方法
- ngx_lua利用nginx内部跳转提高访问效率
- [BZOJ1861][ZJOI2006]书架(平衡树splay)
- Matlab R2014a由.m文件产生C代码,在VS2010中运行
- Linux useradd --添加用户账号
- Android 生命周期中每个函数适合处理的事件
- jiava
- java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig 原因分析与解决方案
- C++实现——大数相加
- C++多线程编程参考
- HBase依赖的tomcat:jasper-compiler和tomcat:jasper-runtime搞得我web页面的JSP访问失败