我感觉我好像大概懂了...Splay预警。
题目 好像也不是很难的亚子。
昨天整个下午瞪眼睛看了个七七八八,今天终于收尾啦。
还是可以接受的???好像真的不太难不要被骗了。
题目大意
您需要写一种数据结构,来维护一些数,需要提供以下操作:
1、插入x数
2、删除x数(若有多个相同的数,因只删除一个)
3、查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4、查询排名为xx的数
5、求x的前驱(前驱定义为小于x,且最大的数)
6、求x的后继(后继定义为大于x,且最小的数)
时空限制:1000ms,128M;
1.n的数据范围:n≤100000;
2.每个数的数据范围:[-10^7,10^7]。
题目分析
安利几篇blogs!!
挺神奇的一玩意儿,转来转去有点意思...
道理不会讲,上面的blogs已经讲得很清楚了。
指针这个玩意儿一直不会用,总感觉挺玄学的(明明是我太弱了好吗)。
所以下面的代码是非指针的。代码里会有些解释。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,root=0,tot=0;
struct node{int x,f,size,c,son[2];}s[100010];
//x:值,f:父亲的编号,size:管理的结点个数,n:相同值的结点个数
void add(int x,int fa)
{
tot++;
s[tot].x=x; s[tot].f=fa;
s[tot].size=1; s[tot].c=1;
s[tot].son[0]=s[tot].son[1]=0;
if(x<s[fa].x) s[fa].son[0]=tot;
else s[fa].son[1]=tot;
}
int findp(int x)
{
int now=root;
while(x!=s[now].x)
{
if(x<s[now].x)
{
if(s[now].son[0]==0) break;
else now=s[now].son[0];
}
else
{
if(s[now].son[1]==0) break;
else now=s[now].son[1];
}
}
return now;
}
void update(int x)
{
s[x].size=s[s[x].son[0]].size+s[s[x].son[1]].size+s[x].c;
//我管理的结点个数=左儿子管理的结点个数+右儿子管理的结点个数+这个值为这个结点的个数
return ;
}
//这两个the most important墙裂推荐上面第一个博客
//能看懂
void rotate(int x,int d)//the most important,too
{
int f=s[x].f,ff=s[f].f;
s[f].son[1-d]=s[x].son[d];
if(s[x].son[d]) s[s[x].son[d]].f=f;
if(s[ff].son[0]==f) s[ff].son[0]=x;
else s[ff].son[1]=x;
s[x].f=ff; s[f].f=x;
s[x].son[d]=f; update(f),update(x);
}
void splay(int x,int fa)//the most important
{
while(s[x].f!=fa)
{
int f=s[x].f,ff=s[f].f;
if(ff==fa)//单旋
{
if(s[f].son[0]==x) rotate(x,1);
else rotate(x,0);
}
else
{
if(s[ff].son[0]==f)
{
if(s[f].son[0]==x) rotate(f,1),rotate(x,1);//双旋
else rotate(x,0),rotate(x,1);
}
else
{
if(s[f].son[0]==x) rotate(x,1),rotate(x,0);
else rotate(f,0),rotate(x,0);//双旋
}
}
}
if(fa==0) root=x;
}
void ins(int x)//插入x
{
//树为空则直接插入,根就是这个点
if(root==0) { add(x,0),root=tot; return ; }
int xx=findp(x);//找值为x的结点编号
if(s[xx].x==x)//已经有值为x的结点
{
s[xx].c++;//相同值得结点个数++
update(xx);//更新值为x的结点管理的结点个数
splay(xx,0);//把这个旋到最上面
}
else//还没有值为x的结点
{
add(x,xx);//插入这个结点
update(xx);//更新这个结点管理的结点个数
splay(tot,0);//把这个旋到最上面
}
}
void del(int x)//删除x
{
int xx=findp(x);
splay(xx,0);//先把这个点旋到上面
if(s[xx].x!=x) return ;
if(s[xx].c>1) s[xx].c--,update(xx);//如果这个值的点不止一个直接--
else if(!s[xx].son[0]&&!s[xx].son[1]) root=0,tot=0;//树上就这一个点
else if(s[xx].son[0]&&!s[xx].son[1]) root=s[xx].son[0],s[s[xx].son[0]].f=0;
//右儿子为空 ,左儿子直接放到根
else if(!s[xx].son[0]&&s[xx].son[1]) root=s[xx].son[1],s[s[xx].son[1]].f=0;
//左儿子为空, 右儿子直接放到根
else
{
int qxx=s[xx].son[0];//x的前驱做根
while(s[qxx].son[1]) qxx=s[qxx].son[1];
splay(qxx,xx); root=qxx; s[qxx].f=0;//x的右儿子成为x的前驱的右儿子
s[s[xx].son[1]].f=qxx; s[qxx].son[1]=s[xx].son[1];
update(qxx);
}
}
int findr(int x)//查询x数的排名
{
int xx=findp(x);
splay(xx,0);//把它旋到根
return s[s[xx].son[0]].size+1;//它的排名为左子树大小+1
}
int findn(int x)//查询排名为x的数
{
//根据左右子树的大小往下找
int now=root;
while(1)
{
if(x<=s[s[now].son[0]].size) now=s[now].son[0];//去左儿子找
else if(x>s[now].c+s[s[now].son[0]].size)//去右儿子找
{
x-=s[now].c+s[s[now].son[0]].size;
now=s[now].son[1];
}
else break;//找到了
}
return s[now].x;
}
int findq(int x)//求x的前驱
{
//左儿子的最右儿子
int xx=findp(x);
splay(xx,0);
if(x<=s[xx].x&&s[xx].son[0])
{
xx=s[xx].son[0];
while(s[xx].son[1]) xx=s[xx].son[1];
}
if(x<=s[xx].x) return 0;
else return s[xx].x;
}
int findh(int x)//求x的后继
{
//右儿子的最左儿子
int xx=findp(x);
splay(xx,0);
if(s[xx].x<=x&&s[xx].son[1])
{
xx=s[xx].son[1];
while(s[xx].son[0]) xx=s[xx].son[0];
}
if(s[xx].x<=x) return 0;
else return s[xx].x;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int opt,x; scanf("%d%d",&opt,&x);
if(opt==1) ins(x);//插入x
else if(opt==2) del(x);//删除x
else if(opt==3) printf("%d\n",findr(x));//查询x数的排名
else if(opt==4) printf("%d\n",findn(x));//查询排名为x的数
else if(opt==5) printf("%d\n",findq(x));//求x的前驱
else if(opt==6) printf("%d\n",findh(x));//求x的后继
}
return 0;
}