您的位置:首页 > 其它

BZOJ 3224 Tyvj 1728 普通平衡树 (Treap)

2017-01-03 19:15 405 查看

BZOJ 3224 Tyvj 1728 普通平衡树

题目概述:

给n个操作,有6种操作:

1.插入一个数

2.删除一个数(若该数有多个,那么只删除一个)

3.查询一个数的排名(若有多个,取最小)

4.查询一个排名对应数

5.查询一个数的前驱(小于该数的最大数)

6.查询一个数的后继(大于该数的最小数)

题目分析:

如题目,是一道平衡树的版题,可以选择使用Treap实现.

注意可能会有相同的数出现,需要记录一下同样的数的个数.

代码:

数组版

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

const int maxn=100000+10;

#define lc ch[u][0]
#define rc ch[u][1]

int ch[maxn][2],key[maxn],sz[maxn],rnd[maxn],cnt[maxn],id,root;

void update(int u)
{
sz[u]=sz[lc]+sz[rc]+cnt[u];
}

void rotate(int& u,int d)//旋转,d 0,left;1,right.
{
int v=ch[u][d^1];
ch[u][d^1]=ch[v][d];
ch[v][d]=u;
update(u);
update(u=v);
}

void insert(int& u,int val)//插入,注意维护结点的子树个数和
{
if(!u) {
u=++id;
lc=rc=0;
key[u]=val;
rnd[u]=rand();
sz[u]=cnt[u]=1;
return ;
}
if(key[u]==val) {++sz[u],++cnt[u];return ;}
int d=key[u]<val;
insert(ch[u][d],val);++sz[u];
if(rnd[ch[u][d]]<rnd[u]) rotate(u,d^1);
}

bool remove(int& u,int val)//删除,注意维护结点的子树个数和
{
if(!u) return false;
if(key[u]==val) {
if(cnt[u]>1) return --sz[u],--cnt[u],true;//不止一个,直接删
if(!lc||!rc) return u=lc?lc:rc,true;//仅有一棵子树与之相连,选择将其填u
if(rnd[lc]>rnd[rc]) rotate(u,0);//若有两棵子树,先旋转处理优先级,再删除
else rotate(u,1);
return remove(u,val);
}
int d=key[u]<val;
if(!remove(ch[u][d],val)) return false;
return --sz[u],true;
}

int find_rnk(int val)//根据key求rank,注意先计算rnk,再更新u
{
int u=root,rnk=1;
while(u) {
if(val<key[u]) u=lc;//val小于根key值,走lc
else if(val>key[u]) rnk+=sz[lc]+cnt[u],u=rc;//val大于根key值,走rc
else return rnk+sz[lc];//val等于根key值,直接返回
}
return 0;
}

int find_key(int rnk)//根据rank求key
{
int u=root;
while(u) {
if(rnk<=sz[lc]) u=lc;
else if(rnk>sz[lc]+cnt[u]) rnk-=sz[lc]+cnt[u],u=rc;
else return key[u];
}
return 0;
}

int find_pre(int val)//前驱
{
int u=root,pre;
while(u) {
if(key[u]<val) pre=key[u],u=rc;
else u=lc;
}
return pre;
}

int find_nxt(int val)//后继
{
int u=root,nxt;
while(u) {
if(key[u]>val) nxt=key[u],u=lc;
else u=rc;
}
return nxt;
}

int main()
{
int n,op,x,cnt=0;
scanf("%d",&n);
while(n--) {
++cnt;
scanf("%d%d",&op,&x);
switch(op) {
case 1:insert(root,x);break;
case 2:remove(root,x);break;
case 3:printf("%d\n",find_rnk(x));break;
case 4:printf("%d\n",find_key(x));break;
case 5:printf("%d\n",find_pre(x));break;
case 6:printf("%d\n",find_nxt(x));break;
}
}
return 0;
}


指针版

之后又手贱写了指针版本,容易RE……不过大致还是类似的,主要是非法地址的处理

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn=100000+10;

#define lc (u->ch[0])
#define rc (u->ch[1])

struct Node {
Node* ch[2];
int key,rnd,sz,cnt;
Node(){ch[0]=ch[1]=NULL;sz=cnt=0;}
Node(int key,int rnd):key(key),rnd(rnd){}
void update() {
sz=ch[0]->sz+ch[1]->sz+cnt;
}
}*root,*null;//引入null来避免访问非法地址

void rotate(Node* &u,int d)//旋转,d 0,left;1,right.
{
Node* v=u->ch[d^1];
u->ch[d^1]=v->ch[d];
v->ch[d]=u;
u->update();
(u=v)->update();
}

void insert(Node* &u,int val)
{
if(!u||u==null) {//root不要和null赋成一样的,避免null中的sz不为0
u=new Node(val,rand());
u->sz=u->cnt=1;
lc=rc=null;
return ;
}
if(val==(u->key)) {++(u->sz),++(u->cnt);return ;}
int d=(u->key)<val;
insert(u->ch[d],val);++(u->sz);
if((u->rnd)>(u->ch[d]->rnd))
rotate(u,d^1);
}

bool remove(Node* &u,int val)
{
if(u==null) return false;
if((u->key)==val) {
Node* t=u;
if(u->cnt>1) return --(u->sz),--(u->cnt),true;
if(lc==null||rc==null) return u=(lc!=null?lc:rc),delete t,true;
if((lc->rnd)>(rc->rnd)) rotate(u,0);
else rotate(u,1);
return remove(u,val);
}
int d=(u->key)<val;
if(!remove(u->ch[d],val)) return false;
return --(u->sz),true;
}

int find_rnk(int val)
{
int rnk=1;
Node* u=root;
while(u!=null) {
if(val<(u->key)) u=lc;
else if(val>(u->key)) rnk+=(lc->sz)+(u->cnt),u=rc;
else return rnk+(lc->sz);
}
return rnk;
}

int find_key(int rnk)
{
Node* u=root;
while(u!=null) {
if(rnk<=(lc->sz)) u=lc;
else if(rnk>(lc->sz)+(u->cnt)) rnk-=(lc->sz)+(u->cnt),u=rc;
else return u->key;
}
return 0;
}

int find_pre(int val)
{
int pre;
Node* u=root;
while(u!=null) {
if(val>(u->key)) pre=(u->key),u=rc;
else u=lc;
}
return pre;
}

int find_nxt(int val)
{
int nxt;
Node* u=root;
while(u!=null) {
if(val<(u->key)) nxt=(u->key),u=lc;
else u=rc;
}
return nxt;
}

int main()
{
null=new Node();//null赋一个地址,不然也会RE
null->ch[0]=null->ch[1]=null;//将null的左右儿子也赋成null
int n,op,x,cnt=0;
scanf("%d",&n);
while(n--) {
++cnt;
scanf("%d%d",&op,&x);
switch(op) {
case 1:insert(root,x);break;
case 2:remove(root,x);break;
case 3:printf("%d\n",find_rnk(x));break;
case 4:printf("%d\n",find_key(x));break;
case 5:printf("%d\n",find_pre(x));break;
case 6:printf("%d\n",find_nxt(x));break;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: