fhq-treap
%%% 范浩强
实现
分裂操作 Split
给定一棵平衡树 \(K\),按照每个权值 \(val\),分裂成平衡树 \(A,B\),且 \(A\) 树所有点的权值不超过 \(val\),\(B\) 树所有点的权值大于 \(val\)。
代码:
void splitt(int cur,int val,int &L,int &R)
{
if(cur==0)
{
L=R=0;
return ;
}
if(tree[cur].val<=val)
{
L=cur;
splitt(tree[cur].rx,val,tree[cur].rx,R); //寻找下一个 <=val 的点,把它拼在右子树
}
else
{
R=cur;
splitt(tree[cur].lx,val,L,tree[cur].lx);
}
pushup(cur); // 更新信息
return ;
}
更新信息
非常简单,更新树的大小 \(siz\) 即可。
void pushup(int cur)
{
tree[cur].siz=tree[tree[cur].lx].siz+tree[tree[cur].rx].siz+1;
return ;
}
合并操作 union
将两颗平衡树 \(A,B\) 合并成一棵平衡树 \(K\),但要保证 \(A\) 树的所有点权小于 \(B\) 树的所有点权。
代码:
void unionn(int &cur,int L,int R)
{
if(L==0||R==0)
{
cur=L^R;
return ;
}
if(tree[L].rnk<tree[R].rnk) //符号任意
{
cur=L;
// R 要成为 L 的右儿子,但是 L 有一个右儿子,于是继续合并
unionn(tree[L].rx,tree[L].rx,R);
}
else
{
cur=R;
unionn(tree[R].lx,L,tree[R].lx);
}
pushup(cur);
return ;
}
新建节点和和插入节点
没什么好说,直接看代码:
int addnew(int val)
{
tree[++tot].val=val;
tree[tot].lx=tree[tot].rx=0;
tree[tot].siz=1;
tree[tot].rnk=rand();
return tot;
}
void insert(int &k,int val)
{
int lt=0,rt=0,nw=addnew(val);
splitt(k,val,lt,rt); // 分裂成 <= val 的和 > val 的
unionn(lt,lt,nw); // 再把 nw 插进去合并
unionn(k,lt,rt);
return ;
}
删除节点
此处我们默认删一个节点。
void delet(int &k,int val)
{
int lt=0,mid=0,rt=0;
splitt(k,val,lt,rt);
splitt(lt,val-1,lt,mid);
// 此时 lt 中 < val,mid 中 = val,rt 中 > val
unionn(mid,tree[mid].lx,tree[mid].rx); // 相当于删掉 mid 这个节点,保留左儿子和右儿子
unionn(lt,lt,mid); //当要删全部时,去掉这一行和上一行即可(把 mid 丢掉)
unionn(k,lt,rt);
return ;
}
查询排名
超级简单:
int get_rank(int &k,int val)
{
int a=0,b=0;
splitt(k,val-1,a,b);
int ans=tree[a].siz+1; // 题目要求加一
unionn(k,a,b);
return ans;
}
查询数值
比其它函数(除了分裂与合并)复杂,非递归版代码:
int get_num(int k,int rk)
{
while(tree[tree[k].lx].siz+1!=rk)
{
if(tree[tree[k].lx].siz>=rk)
k=tree[k].lx;
else
{
rk-=tree[tree[k].lx].siz+1;
k=tree[k].rx;
}
}
return tree[k].val;
}
递归版代码:
int get_num(int k,int rk)
{
if(tree[tree[k].lx].siz+1==rk)
return tree[k].val;
if(tree[tree[k].lx].siz>=rk)
return get_num(tree[k].lx,rk);
return get_num(tree[k].rx,rk-(tree[tree[k].lx].siz+1));
}
前驱与后继
利用前面的函数,真的好写:
int prev(int &k,int val)
{
int a=0,b=0;
splitt(k,val-1,a,b);
int ans=get_num(a,tree[a].siz); //找到最大的
unionn(k,a,b);
return ans;
}
int next(int &k,int val)
{
int a=0,b=0;
splitt(k,val,a,b);
int ans=get_num(b,1); //找到最小的
unionn(k,a,b);
return ans;
}
总代码
结合以下,我们的 P3369 就写完了:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
class BST
{
public:
int val,lx,rx,siz,rnk;
}tree[N];
int tot,n,root;
void pushup(int cur) //更新信息
{
tree[cur].siz=tree[tree[cur].lx].siz+tree[tree[cur].rx].siz+1;
return ;
}
void splitt(int cur,int val,int &L,int &R) //分裂成一个权值 <= val 的和一个 > val的
{
if(cur==0)
{
L=R=0;
return ;
}
if(tree[cur].val<=val)
{
L=cur;
splitt(tree[cur].rx,val,tree[cur].rx,R); //寻找下一个 <=val 的点,把它拼在右子树
}
else
{
R=cur;
splitt(tree[cur].lx,val,L,tree[cur].lx);
}
pushup(cur); // 更新信息
return ;
}
void unionn(int &cur,int L,int R) //合并,但要保证 L 的所有权值 <= R 的所有权值
{
if(L==0||R==0)
{
cur=L^R;
return ;
}
if(tree[L].rnk<tree[R].rnk) //符号任意
{
cur=L;
// R 要成为 L 的右儿子,但是 L 有一个右儿子,于是继续合并
unionn(tree[L].rx,tree[L].rx,R);
}
else
{
cur=R;
unionn(tree[R].lx,L,tree[R].lx);
}
pushup(cur);
return ;
}
int addnew(int val) //新建节点
{
tree[++tot].val=val;
tree[tot].lx=tree[tot].rx=0;
tree[tot].siz=1;
tree[tot].rnk=rand();
return tot;
}
void insert(int &k,int val) //插入节点
{
int lt=0,rt=0,nw=addnew(val);
splitt(k,val,lt,rt); // 分裂成 <= val 的和 > val 的
unionn(lt,lt,nw); // 再把 nw 插进去合并
unionn(k,lt,rt);
return ;
}
void delet(int &k,int val) //删除节点
{
int lt=0,mid=0,rt=0;
splitt(k,val,lt,rt);
splitt(lt,val-1,lt,mid);
// 此时 lt 中 < val,mid 中 = val,rt 中 > val
unionn(mid,tree[mid].lx,tree[mid].rx); // 相当于删掉 mid 这个节点,保留左儿子和右儿子
unionn(lt,lt,mid); //当要删全部时,去掉这一行和上一行即可(把 mid 丢掉)
unionn(k,lt,rt);
return ;
}
int get_rank(int &k,int val) //查询排名并 +1
{
int a=0,b=0;
splitt(k,val-1,a,b);
int ans=tree[a].siz+1; // 题目要求加一
unionn(k,a,b);
return ans;
}
int get_num(int k,int rk) //查询数值
{
while(tree[tree[k].lx].siz+1!=rk)
{
if(tree[tree[k].lx].siz>=rk)
k=tree[k].lx;
else
{
rk-=tree[tree[k].lx].siz+1;
k=tree[k].rx;
}
}
return tree[k].val;
}
//int get_num(int k,int rk)
//{
// if(tree[tree[k].lx].siz+1==rk)
// return tree[k].val;
// if(tree[tree[k].lx].siz>=rk)
// return get_num(tree[k].lx,rk);
// return get_num(tree[k].rx,rk-(tree[tree[k].lx].siz+1));
//}
int prev(int &k,int val) //前驱
{
int a=0,b=0;
splitt(k,val-1,a,b);
int ans=get_num(a,tree[a].siz); //找到最大的
unionn(k,a,b);
return ans;
}
int next(int &k,int val) //后继
{
int a=0,b=0;
splitt(k,val,a,b);
int ans=get_num(b,1); //找到最小的
unionn(k,a,b);
return ans;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
srand(time(0));
cin>>n;
while(n--)
{
int opt,x;
cin>>opt>>x;
if(opt==1)
insert(root,x);
else if(opt==2)
delet(root,x);
else if(opt==3)
cout<<get_rank(root,x)<<"\n";
else if(opt==4)
cout<<get_num(root,x)<<"\n";
else if(opt==5)
cout<<prev(root,x)<<"\n";
else if(opt==6)
cout<<next(root,x)<<"\n";
else
return 114514;
}
return 0;
}

浙公网安备 33010602011771号