FHQ Treap

2022年10月3日

这波我吹爆 fhq treap,代码简短功能强大不易写错。

据说常数大一点?我不知道啊,反正能写出来就是胜利(?)。

SP4487 GSS6 - Can you answer these queries VI

点击查看代码
/*
* Author: ShaoJia
* Last Modified time: 2022-10-03 21:28:32
* Motto: We'll be counting stars.
*/
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define debug(...) cerr<<"#"<<__LINE__<<": "<<__VA_ARGS__<<endl
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define lob lower_bound
#define upb upper_bound
#define fir first
#define sec second
#define mkp make_pair
#define siz(x) ((int)(x).size())
#define pb emplace_back
#define ckmx(a,b) a=max(a,b)
#define ckmn(a,b) a=min(a,b)
#define ll long long
#define pi pair<int,int>
#define N 200010
mt19937 rgen(time(0)+size_t(new(char)));
struct node{
int sz,l,r,sum,ans;
node(){}
node(int x){ sz=1,l=r=sum=ans=x; }
node(int x,int y,int z,int w,int aa){ sz=x,l=y,r=z,sum=w,ans=aa; }
friend node operator+(const node& x,const node& y){
return node(x.sz+y.sz,
max(x.l,x.sum+y.l),
max(y.r,y.sum+x.r),
x.sum+y.sum,
max({x.ans,y.ans,x.r+y.l})
);
}
}t[N];
int ls[N],rs[N],w[N],v[N],root=0,tot=0;
void upd(int rt){
t[rt]=node(v[rt]);
if(ls[rt]) t[rt]=t[ls[rt]]+t[rt];
if(rs[rt]) t[rt]=t[rt]+t[rs[rt]];
}
int nnd(int val){
t[++tot]=node(val);
v[tot]=val;
ls[tot]=rs[tot]=0;
w[tot]=rgen();
return tot;
}
int merge(int x,int y){
// cout<<x<<"~"<<y<<"\n";
if(!x || !y) return x^y;
if(w[x]>w[y]){
rs[x]=merge(rs[x],y);
upd(x);
return x;
}else{
ls[y]=merge(x,ls[y]);
upd(y);
return y;
}
}
void split(int rt,int k,int &l,int &r){
if(!rt) return l=r=0,void();
if(k<=t[ls[rt]].sz) r=rt,split(ls[rt],k,l,ls[rt]);
else l=rt,split(rs[rt],k-t[ls[rt]].sz-1,rs[rt],r);
upd(rt);
}
void print(int rt){
if(!rt) return ;
print(ls[rt]);
cout<<v[rt]<<" ";
print(rs[rt]);
}
signed main(){ios::sync_with_stdio(false),cin.tie(nullptr);
int cnt,x,y,a,b,c,tmp;
char opt;
cin>>cnt;
while(cnt--){
cin>>x;
// print(root); cout<<"\n";
root=merge(root,nnd(x));
}
cin>>cnt;
while(cnt--){
// print(root);
// cout<<"\n";
cin>>opt>>x;
if(opt=='I'){
cin>>y;
split(root,x-1,a,b);
root=merge(merge(a,nnd(y)),b);
}else if(opt=='D'){
split(root,x,c,b);
split(c,x-1,a,tmp);
root=merge(a,b);
}else if(opt=='R'){
cin>>y;
split(root,x,c,b);
split(c,x-1,a,tmp);
root=merge(a,merge(nnd(y),b));
}else{
cin>>y;
split(root,y,tmp,c);
split(tmp,x-1,a,b);
// print(b); cout<<"----\n";
cout<<t[b].ans<<"\n";
root=merge(a,merge(b,c));
}
}
return 0;}

远古

本质:无 rotate 平衡树

一旦没了 rotate,代码就短好多,思路也清晰。

首先说一下,这个东西可以搞一切 bst , treap , splay 所能搞的东西。
——自为风月马前卒

例题:P3369 【模板】普通平衡树

我的提交

树的基本东西(正常):

struct node{
int sz;//树的大小
int w;//随机数满足大顶堆
int val;//Key_value
int ls,rs;//左右儿子
}t[N];
int root=0;//整棵树的根
int tot=0;//类似前向星,存最大下标
void upd(int rt){//更新 sz
t[rt].sz=t[t[rt].ls].sz+t[t[rt].rs].sz+1;
}
int nwnd(int x){//新建节点
t[++tot]={1,rand(),x,0,0};
return tot;
}

整个数据结构中只有 2 种操作、1 种询问:

  • split 把一棵树分成两棵树。
//将 rt 为根的树分成 <=val 和 >val 两部分,两个根节点存入 l 和 r
void split(int rt,int val,int &l,int &r){
if(!rt){l=r=0;return ;}//没了!?
if(t[rt].val<=val) l=rt,split(t[rt].rs,val,t[rt].rs,r);//右边开裂
else r=rt,split(t[rt].ls,val,l,t[rt].ls);//左边开裂
upd(rt);
}
  • merge 把两棵树合成一棵树。
//将根为 x 和 y 的树合并
int merge(int x,int y){
if(!x || !y) return x+y;//一方没了
if(t[x].w>t[y].w) {t[x].rs=merge(t[x].rs,y);upd(x);return x;}//x 为根
else {t[y].ls=merge(x,t[y].ls);upd(y);return y;}//y 为根
}
  • kth 查找一棵树里的第 k 小(二分查找)。
//查找根为 rt 的树内 val 第 x 小的位置
int kth(int rt,int x){
int rk;
while(true){
rk=t[t[rt].ls].sz+1;
if(x==rk) return rt;
if(x<rk) rt=t[rt].ls;
else x-=rk, rt=t[rt].rs;
}
}

就结束了……

我们看看各项操作是如何通过上述操作实现的:

  • 插入 x
split(root,x,a,b);
root=merge(merge(a,nwnd(x)),b);
  • 删除 x(若有多个相同的数,只删除一个)。
split(root,x,a,c);
split(a,x-1,a,b);
root=merge(merge(a,t[b].ls),merge(t[b].rs,c));
  • 查询 x 的排名(排名定义为比当前数小的数的个数 +1)。
split(root,x-1,a,b);
cout<<t[a].sz+1<<endl;
root=merge(a,b);
  • 查询排名为 x 的数。
cout<<t[kth(root,x)].val<<endl;
  • x 的前驱。
split(root,x-1,a,b);
cout<<t[kth(a,t[a].sz)].val<<endl;
root=merge(a,b);
  • x 的后继。
split(root,x,a,b);
cout<<t[kth(b,1)].val<<endl;
root=merge(a,b);

最后说一下,FHQ 其实是可以处理区间问题的(像 Splay 一样)。

posted @   ShaoJia  阅读(34)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示