- qucode's Note
Treap树讲解少林
- 2023-11-5 11:36:35 @
https://note.youdao.com/s/cmbAyLMG
#include<cstdio>
#include<iostream>
#include<ctime>
using namespace std;
int id[5000005];
struct Node{
int size;//以这个结点为根的子树的结点总数,用于名次树
int rank;//优先级,用于维持树的平衡性,优先级越高则结点在树的上层
int key;//键值,表示结点的值
Node *son[2];//son[0]表示左子树,son[1]表示右子树
//构造函数,用于初始化结点
Node(int size,int rank,int key):size(size),rank(rank),key(key){
son[0]=son[1]=NULL;
}
//重载小于运算符,用于比较结点的优先级
bool operator<(const Node &a) const{
return rank<a.rank;
}
//比较函数,用于判断插入结点的位置
int cmp(int x) const{
if(x==key) return -1;//返回-1表示键值相等
return x<key?0:1;//返回0表示插入到左子树,返回1表示插入到右子树
}
//更新结点的size属性,表示该结点为根的子树的结点总数
void update(){
size=1;
if(son[0]!=NULL) size+=son[0]->size;
if(son[1]!=NULL) size+=son[1]->size;
}
};
//d=0左旋,d=1右旋
void rotate(Node *&o,int d){
Node *k=o->son[d^1];//记录当前结点的另一个子树,d^1与1-d等价,但是更快。
o->son[d^1]=k->son[d];//将当前结点的另一子树的相应子树更新为k的相应子树
k->son[d]=o;//将当前结点连接到k的相应子树上
o->update();//更新当前结点的size属性
k->update();//更新k结点的size属性
o=k;//更新当前结点为k结点,实现旋转操作
}
//把x插入到树中
void insert(Node *&o,int x){
if(o==NULL) o=new Node(1,rand(),x);//如果结点为空,创建键值为x,优先级为随机树的结点
else{
int d=o->cmp(x);//比较当前结点的键值和要插入结点的键值x,-1相等,0表示大于x,1表示小于x
insert(o->son[d],x);//递归插入结点到当前结点的左子树或右子树
o->update();//更新当前结点的size
if(o<o->son[d])//如果当前结点的优先级小于它的子节点的优先级
rotate(o,d^1);//进行旋转操作,维持这棵树的平衡性
}
}
//返回第k大的数
int kth(Node *o,int k){
//如果当前结点为空,或者k小于等于0,或者k大于当前结点子树的结点数,返回-1
if(o==NULL||k<=0||k>o->size) return -1;
//用于计算当前结点的右子树的结点数量
int s=o->son[1]==NULL?0:o->son[1]->size;
if(k==s+1) return o->key;//如果k等于当前结点的右子树结点数量+1,表示当前结点即为第k大的数
else if(k<=s) return kth(o->son[1],k);//如果k小于等于当前结点的右子树结点数量,递归在右子树中找第k大的数
else return kth(o->son[0],k-s-1);//如果k大于当前结点的左子树结点数量,递归在左子树中找第k大的数
}
//返回元素k的名次
int find(Node *o,int k){
if(o==NULL) return -1;
int d=o->cmp(k);//比较当前结点的键值和目标值x,-1相等 0当结点大于k 1当前结点小于k
if(d==-1)
return o->son[1]==NULL?1:o->son[1]->size+1;
else if(d==1)
return find(o->son[1],k);
else{
int temp=find(o->son[0],k);
if(temp==-1) return -1;
else return o->son[1]==NULL?temp+1:temp+1+o->son[1]->size;
}
}
int main(){
int n;
while(~scanf("%d",&n)&&n){
//使用当前时间作为随机数种子,让每次程序运行时得到的随机数序列不同
srand(time(NULL));
int k,g;
scanf("%d %d",&k,&g);
Node *root=new Node(1,rand(),g);//创建根结点,键值为g,优先级是随机数
id[g]=k;//将结点的编号与名次存入id数组中
printf("%d 1\n",k);
//循环读取,构建名次树(Treap)并输出每个结点的编号和名次
for(int i=2;i<=n;i++){
scanf("%d %d",&k,&g);
id[g]=k;
insert(root,g);//插入结点到名次数中
int t=find(root,g);//返回新插入结点的名次
int ans1,ans2,ans;
ans1=kth(root,t-1);//前一位老和尚
ans2=kth(root,t+1);//后一位老和尚
if(ans1!=-1&&ans2!=-1)
ans=ans1-g>=g-ans2?ans2:ans1;//判断前一名和后一名中与当前结点距离更近的结点。
else if(ans1==-1) ans=ans2;
else ans=ans1;
printf("%d %d\n",k,id[ans]);//输出编号和名次
}
}
return 0;
}